//auto update last change
var lastchangetime = null;
var lastchangeui = null;
function updateLastChange() {
if (lastchangetime && lastchangeui) {
lastchangeui.html(' 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) {
title = h1s.first().text();
} else {
title = null;
}
return title;
}
//render title
function renderTitle(view) {
var title = getTitle(view);
if (title) {
title += ' - HackMD';
} else {
title = 'HackMD - Collaborative notes';
}
return title;
}
//render filename
function renderFilename(view) {
var filename = getTitle(view);
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 extra tags
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");
function replaceExtraTags(html) {
html = html.replace(coloregex, '');
html = html.replace(nameandtimeregex, ' $1 $2');
html = html.replace(nameregex, ' $1');
html = html.replace(timeregex, ' $1');
return html;
}
//dynamic event or object binding here
function finishView(view) {
//todo list
var lis = view.find('li.raw').removeClass("raw").sortByDepth().toArray();
for (var i = 0; i < lis.length; i++) {
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];
}
html = replaceExtraTags(html);
li.innerHTML = html;
var disabled = 'disabled';
if(typeof editor !== 'undefined' && havePermission())
disabled = '';
if (/^\s*\[[x ]\]\s*/.test(html)) {
li.innerHTML = html.replace(/^\s*\[ \]\s*/, '')
.replace(/^\s*\[x\]\s*/, '');
lis[i].setAttribute('class', 'task-list-item');
}
if (typeof editor !== 'undefined' && havePermission())
$(li).find('input').change(toggleTodoEvent);
//color tag in list will convert it to tag icon with color
var tag_color = $(li).closest('ul').find(".color");
tag_color.each(function (key, value) {
$(value).addClass('fa fa-tag').css('color', $(value).attr('data-color'));
});
}
//youtube
view.find(".youtube.raw").removeClass("raw")
.click(function () {
imgPlayiframe(this, '//www.youtube.com/embed/');
});
//vimeo
view.find(".vimeo.raw").removeClass("raw")
.click(function () {
imgPlayiframe(this, '//player.vimeo.com/video/');
})
.each(function (key, value) {
$.ajax({
type: 'GET',
url: '//vimeo.com/api/v2/video/' + $(value).attr('videoid') + '.json',
jsonp: 'callback',
dataType: 'jsonp',
success: function (data) {
var thumbnail_src = data[0].thumbnail_large;
var image = '';
$(value).prepend(image);
}
});
});
//gist
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.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);
}
} catch (err) {}
//sequence diagram
var sequences = view.find(".sequence-diagram.raw").removeClass("raw");
sequences.each(function (key, value) {
try {
var sequence = $(value);
sequence.sequenceDiagram({
theme: 'simple'
});
sequence.parent().parent().replaceWith(sequence);
} catch (err) {
console.error(err);
}
});
//flowchart
var flow = view.find(".flow-chart.raw").removeClass("raw");
flow.each(function (key, value) {
try {
var chart = flowchart.parse($(value).text());
$(value).html('');
chart.drawSVG(value, {
'line-width': 2,
'fill': 'none',
'font-size': '16px',
'font-family': "'Andale Mono', monospace"
});
$(value).parent().parent().replaceWith(value);
} catch (err) {
console.error(err);
}
});
//graphviz
var graphvizs = view.find(".graphviz.raw").removeClass("raw");
graphvizs.each(function (key, value) {
try {
var graphviz = Viz($(value).text());
$(value).html(graphviz);
$(value).parent().parent().replaceWith(value);
} catch (err) {
console.error(err);
}
});
//image href new window(emoji not included)
var images = view.find("img.raw[src]").removeClass("raw");
images.each(function (key, value) {
var src = $(value).attr('src');
var a = $('');
if (src) {
a.attr('href', src);
a.attr('target', "_blank");
}
var clone = $(value).clone();
clone[0].onload = function (e) {
if(viewAjaxCallback)
viewAjaxCallback();
};
a.html(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 = replaceExtraTags(html);
$(value).html(html);
});
//color tag in blockquote will change its left border color
var blockquote_color = blockquote.find(".color");
blockquote_color.each(function (key, value) {
$(value).closest("blockquote").css('border-left-color', $(value).attr('data-color'));
});
//slideshare
view.find(".slideshare.raw").removeClass("raw")
.each(function (key, value) {
$.ajax({
type: 'GET',
url: '//www.slideshare.net/api/oembed/2?url=http://www.slideshare.net/' + $(value).attr('slideshareid') + '&format=json',
jsonp: 'callback',
dataType: 'jsonp',
success: function (data) {
$(value).html(data.html);
}
});
});
//speakerdeck
view.find(".speakerdeck.raw").removeClass("raw")
.each(function (key, value) {
var url = 'https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F' + encodeURIComponent($(value).attr('speakerdeckid'));
//use yql because speakerdeck not support jsonp
$.ajax({
url: 'https://query.yahooapis.com/v1/public/yql',
data: {
q: "select * from json where url ='" + url + "'",
format: "json"
},
dataType: "jsonp",
success: function (data) {
var json = data.query.results.json;
var html = json.html;
var ratio = json.height / json.width;
$(value).html(html);
var iframe = $(value).children('iframe');
var src = iframe.attr('src');
if (src.indexOf('//') == 0)
iframe.attr('src', 'https:' + src);
iframe.css('max-width', '100%');
iframe.attr('width', '540').attr('height', (540 * ratio) + 15);
}
});
});
//render title
document.title = renderTitle(view);
}
//only static transform should be here
function postProcess(code) {
var result = $('
\n'; }; md.renderer.rules.hardbreak = function (tokens, idx, options /*, env */ ) { return md.options.xhtmlOut ? '
' : '
'; }; 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(/\=$|\=\d+$|\=\+$/, '') + ' hljs"'; } if (options.highlight) { highlighted = options.highlight(token.content, langName) || Remarkable.utils.escapeHtml(token.content); } else { highlighted = Remarkable.utils.escapeHtml(token.content); } return '' + md.renderer.getBreak(tokens, idx); }; //youtube var youtubePlugin = new Plugin( // regexp to match /{%youtube\s*([\d\D]*?)\s*%}/, // this function will be called when something matches function (match, utils) { var videoid = match[1]; if (!videoid) return; var div = $(''); setSizebyAttr(div, div); div.attr('videoid', videoid); var thumbnail_src = '//img.youtube.com/vi/' + videoid + '/hqdefault.jpg'; var image = ''; div.append(image); var icon = ''; div.append(icon); return div[0].outerHTML; } ); //vimeo var vimeoPlugin = new Plugin( // regexp to match /{%vimeo\s*([\d\D]*?)\s*%}/, // this function will be called when something matches function (match, utils) { var videoid = match[1]; if (!videoid) return; var div = $(''); setSizebyAttr(div, div); div.attr('videoid', videoid); var icon = ''; div.append(icon); return div[0].outerHTML; } ); //gist var gistPlugin = new Plugin( // regexp to match /{%gist\s*([\d\D]*?)\s*%}/, // this function will be called when something matches function (match, utils) { var gistid = match[1]; var code = '' + highlighted + '
'; return code; } ); //mathjax var mathjaxPlugin = new Plugin( // regexp to match /^\$\$\n([\d\D]*?)\n\$\$$|\$([\d\D]*?)\$/, // this function will be called when something matches function (match, utils) { if (match.index == 0) return '' + match[0] + ''; else return match.input.slice(0, match[0].length); } ); //TOC var tocPlugin = new Plugin( // regexp to match /^\[TOC\]$/, // this function will be called when something matches function (match, utils) { return ''; } ); //slideshare var slidesharePlugin = new Plugin( // regexp to match /{%slideshare\s*([\d\D]*?)\s*%}/, // this function will be called when something matches function (match, utils) { var slideshareid = match[1]; var div = $(''); div.attr('slideshareid', slideshareid); return div[0].outerHTML; } ); //speakerdeck var speakerdeckPlugin = new Plugin( // regexp to match /{%speakerdeck\s*([\d\D]*?)\s*%}/, // this function will be called when something matches function (match, utils) { var speakerdeckid = match[1]; var div = $(''); div.attr('speakerdeckid', speakerdeckid); return div[0].outerHTML; } ); md.use(youtubePlugin); md.use(vimeoPlugin); md.use(gistPlugin); md.use(mathjaxPlugin); md.use(tocPlugin); md.use(slidesharePlugin); md.use(speakerdeckPlugin);