summaryrefslogtreecommitdiff
path: root/public/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
parentf7f8c901f4bc39c3ed0a2bdfe1cbaa1ee6957999 (diff)
Jump to 0.3.1
Diffstat (limited to 'public/js')
-rw-r--r--public/js/cover.js130
-rw-r--r--public/js/extra.js293
-rw-r--r--public/js/fb.js8
-rw-r--r--public/js/history.js3
-rw-r--r--public/js/index.js541
-rw-r--r--public/js/pretty.js84
-rw-r--r--public/js/syncscroll.js13
7 files changed, 878 insertions, 194 deletions
diff --git a/public/js/cover.js b/public/js/cover.js
index 322768bf..49fcddeb 100644
--- a/public/js/cover.js
+++ b/public/js/cover.js
@@ -4,12 +4,17 @@ var options = {
<span class="id" style="display:none;"></span>\
<a href="#">\
<div class="item">\
- <div class="ui-history-close fa fa-close fa-fw"></div>\
- <h4 class="text"></h4>\
- <p><i class="fromNow"><i class="fa fa-clock-o"></i></i>\
- <br>\
- <i class="timestamp" style="display:none;"></i><i class="time"></i></p>\
- <p class="tags"></p>\
+ <div class="ui-history-close fa fa-close fa-fw" data-toggle="modal" data-target=".delete-modal"></div>\
+ <div class="content">\
+ <h4 class="text"></h4>\
+ <p>\
+ <i><i class="fa fa-clock-o"></i> visit </i><i class="fromNow"></i>\
+ <br>\
+ <i class="timestamp" style="display:none;"></i>\
+ <i class="time"></i>\
+ </p>\
+ <p class="tags"></p>\
+ </div>\
</div>\
</a>\
</li>'
@@ -114,16 +119,53 @@ function parseHistoryCallback(list, notehistory) {
$(".ui-history-close").click(function (e) {
e.preventDefault();
var id = $(this).closest("a").siblings("span").html();
+ var value = list.get('id', id)[0].values();
+ $('.ui-delete-modal-msg').text('Do you really want to delete below history?');
+ $('.ui-delete-modal-item').html('<i class="fa fa-file-text"></i> ' + value.text + '<br><i class="fa fa-clock-o"></i> ' + value.time);
+ clearHistory = false;
+ deleteId = id;
+ });
+ buildTagsFilter(filtertags);
+}
+
+//auto update item fromNow every minutes
+setInterval(updateItemFromNow, 60000);
+
+function updateItemFromNow() {
+ var items = $('.item').toArray();
+ for (var i = 0; i < items.length; i++) {
+ var item = $(items[i]);
+ var timestamp = parseInt(item.find('.timestamp').text());
+ item.find('.fromNow').text(moment(timestamp).fromNow());
+ }
+}
+
+var clearHistory = false;
+var deleteId = null;
+
+function deleteHistory() {
+ if (clearHistory) {
+ saveHistory([]);
+ historyList.clear();
+ checkHistoryList();
+ } else {
+ if (!deleteId) return;
getHistory(function (notehistory) {
- var newnotehistory = removeHistory(id, notehistory);
+ var newnotehistory = removeHistory(deleteId, notehistory);
saveHistory(newnotehistory);
});
- list.remove('id', id);
+ historyList.remove('id', deleteId);
checkHistoryList();
- });
- buildTagsFilter(filtertags);
+ }
+ $('.delete-modal').modal('hide');
+ clearHistory = false;
+ deleteId = null;
}
+$(".ui-delete-modal-confirm").click(function () {
+ deleteHistory();
+});
+
$(".ui-import-from-browser").click(function () {
saveStorageHistoryToServer(function () {
parseStorageToHistory(historyList, parseHistoryCallback);
@@ -160,9 +202,10 @@ $(".ui-open-history").bind("change", function (e) {
});
$(".ui-clear-history").click(function () {
- saveHistory([]);
- historyList.clear();
- checkHistoryList();
+ $('.ui-delete-modal-msg').text('Do you really want to clear all history?');
+ $('.ui-delete-modal-item').html('There is no turning back.');
+ clearHistory = true;
+ deleteId = null;
});
$(".ui-refresh-history").click(function () {
@@ -230,6 +273,67 @@ var template = Handlebars.compile(source);
var context = {
release: [
{
+ version: "0.3.1",
+ tag: "clearsky",
+ date: moment("201506301600", 'YYYYMMDDhhmm').fromNow(),
+ detail: [
+ {
+ title: "Features",
+ item: [
+ "+ Added auto table of content",
+ "+ Added basic permission control",
+ "+ Added view count in share note"
+ ]
+ },
+ {
+ title: "Enhancements",
+ item: [
+ "* Toolbar now will hide in single view",
+ "* History time now will auto update",
+ "* Smooth scroll on anchor changed",
+ "* Updated video style"
+ ]
+ },
+ {
+ title: "Fixes",
+ item: [
+ "* Note might not clear when all users disconnect",
+ "* Blockquote tag not parsed properly",
+ "* History style not correct"
+ ]
+ }
+ ]
+ },
+ {
+ version: "0.3.0",
+ tag: "sunrise",
+ date: moment("201506152400", 'YYYYMMDDhhmm').fromNow(),
+ detail: [
+ {
+ title: "Enhancements",
+ item: [
+ "* Used short url in share notes",
+ "* Added upload image button on toolbar",
+ "* Share notes are now SEO and mobile friendly",
+ "* Updated code block style",
+ "* Newline now will cause line breaks",
+ "* Image now will link out",
+ "* Used otk to avoid race condition",
+ "* Used hash to avoid data inconsistency",
+ "* Optimized server realtime script"
+ ]
+ },
+ {
+ title: "Fixes",
+ item: [
+ "* Composition input might lost or duplicated when other input involved",
+ "* Note title might not save properly",
+ "* Todo list not render properly"
+ ]
+ }
+ ]
+ },
+ {
version: "0.2.9",
tag: "wildfire",
date: moment("201505301400", 'YYYYMMDDhhmm').fromNow(),
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);
diff --git a/public/js/fb.js b/public/js/fb.js
deleted file mode 100644
index 0bb7a466..00000000
--- a/public/js/fb.js
+++ /dev/null
@@ -1,8 +0,0 @@
-(function (d, s, id) {
- var js, fjs = d.getElementsByTagName(s)[0];
- if (d.getElementById(id)) return;
- js = d.createElement(s);
- js.id = id;
- js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.3&appId=1436904003272070";
- fjs.parentNode.insertBefore(js, fjs);
-}(document, 'script', 'facebook-jssdk')); \ No newline at end of file
diff --git a/public/js/history.js b/public/js/history.js
index c5db94c5..15e46cc6 100644
--- a/public/js/history.js
+++ b/public/js/history.js
@@ -319,8 +319,9 @@ function parseToHistory(list, notehistory, callback) {
else if (notehistory && notehistory.length > 0) {
for (var i = 0; i < notehistory.length; i++) {
//parse time to timestamp and fromNow
- notehistory[i].timestamp = moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a').unix();
+ notehistory[i].timestamp = moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a').valueOf();
notehistory[i].fromNow = moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a').fromNow();
+ notehistory[i].time = moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a').format('llll');
if (list.get('id', notehistory[i].id).length == 0)
list.add(notehistory[i]);
}
diff --git a/public/js/index.js b/public/js/index.js
index 24b38f75..5f7e6992 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -1,16 +1,21 @@
//constant vars
//settings
var debug = false;
-var version = '0.2.9';
+var version = '0.3.1';
var defaultTextHeight = 18;
var viewportMargin = 20;
var defaultExtraKeys = {
+ "Cmd-S": function () {
+ return CodeMirror.PASS
+ },
+ "Ctrl-S": function () {
+ return CodeMirror.PASS
+ },
"Enter": "newlineAndIndentContinueMarkdownList"
};
var idleTime = 300000; //5 mins
-var doneTypingDelay = 400;
var finishChangeDelay = 400;
var cursorActivityDelay = 50;
var cursorAnimatePeriod = 100;
@@ -97,12 +102,27 @@ var supportExternals = [
search: 'gist'
}
];
-var supportGenerals = [
+var supportBlockquoteTags = [
+ {
+ text: '[name tag]',
+ search: '[]',
+ command: function () {
+ return '[name=' + personalInfo.name + ']';
+ },
+ },
{
+ text: '[time tag]',
+ search: '[]',
command: function () {
- return moment().format('llll');
+ return '[time=' + moment().format('llll') + ']';
},
- search: 'time'
+ },
+ {
+ text: '[color tag]',
+ search: '[]',
+ command: function () {
+ return '[color=' + personalInfo.color + ']';
+ }
}
];
var modeType = {
@@ -131,6 +151,7 @@ var defaultMode = modeType.both;
//global vars
var loaded = false;
+var needRefresh = false;
var isDirty = false;
var editShown = false;
var visibleXS = false;
@@ -192,7 +213,7 @@ var editor = CodeMirror.fromTextArea(textit, {
extraKeys: defaultExtraKeys,
readOnly: true
});
-inlineAttachment.editors.codemirror4.attach(editor);
+var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor);
defaultTextHeight = parseInt($(".CodeMirror").css('line-height'));
//ui vars
@@ -203,7 +224,7 @@ var ui = {
shortStatus: $(".ui-short-status"),
status: $(".ui-status"),
new: $(".ui-new"),
- pretty: $(".ui-pretty"),
+ share: $(".ui-share"),
download: {
markdown: $(".ui-download-markdown")
},
@@ -217,7 +238,24 @@ var ui = {
mode: $(".ui-mode"),
edit: $(".ui-edit"),
view: $(".ui-view"),
- both: $(".ui-both")
+ both: $(".ui-both"),
+ uploadImage: $(".ui-upload-image")
+ },
+ infobar: {
+ lastchange: $(".ui-lastchange"),
+ permission: {
+ permission: $(".ui-permission"),
+ label: $(".ui-permission-label"),
+ freely: $(".ui-permission-freely"),
+ editable: $(".ui-permission-editable"),
+ locked: $(".ui-permission-locked")
+ }
+ },
+ toc: {
+ toc: $('.ui-toc'),
+ affix: $('.ui-affix-toc'),
+ label: $('.ui-toc-label'),
+ dropdown: $('.ui-toc-dropdown')
},
area: {
edit: $(".ui-edit-area"),
@@ -263,10 +301,16 @@ function idleStateChange() {
updateOnlineStatus();
}
-loginStateChangeEvent = function () {
- location.reload(true);
+function setNeedRefresh() {
+ $('#refreshModal').modal('show');
+ needRefresh = true;
+ editor.setOption('readOnly', true);
+ socket.disconnect();
+ showStatus(statusType.offline);
}
+loginStateChangeEvent = setNeedRefresh;
+
//visibility
var wasFocus = false;
Visibility.change(function (e, state) {
@@ -315,6 +359,11 @@ $(document).ready(function () {
$body.removeClass('fixfixed');
});
}
+ //showup
+ $().showUp('.navbar', {
+ upClass: 'navbar-hide',
+ downClass: 'navbar-show'
+ });
});
//when page resize
var windowResizeDelay = 200;
@@ -327,12 +376,38 @@ $(window).resize(function () {
});
//when page unload
$(window).unload(function () {
- emitRefresh();
+ emitUpdate();
});
+//when page hash change
+window.onhashchange = locationHashChanged;
+
+function locationHashChanged(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ if (currentMode != modeType.both) {
+ return;
+ }
+ var hashtarget = $("[id$='" + location.hash.substr(1) + "']");
+ if (hashtarget.length > 0) {
+ var linenumber = hashtarget.attr('data-startline');
+ if (linenumber) {
+ editor.setOption('viewportMargin', Infinity);
+ editor.setOption('viewportMargin', viewportMargin);
+ var t = editor.charCoords({
+ line: linenumber,
+ ch: 0
+ }, "local").top;
+ editor.scrollTo(null, t - defaultTextHeight * 1.2);
+ }
+ }
+}
+
function windowResize() {
checkResponsive();
checkEditorStyle();
+ checkTocStyle();
+ //refresh editor
if (loaded) {
editor.setOption('viewportMargin', Infinity);
setTimeout(function () {
@@ -373,6 +448,39 @@ function checkEditorStyle() {
}
}
+function checkTocStyle() {
+ //toc right
+ var paddingRight = parseFloat(ui.area.markdown.css('padding-right'));
+ var right = ($(window).width() - (ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - paddingRight));
+ ui.toc.toc.css('right', right + 'px');
+ //affix toc left
+ var newbool;
+ var rightMargin = (ui.area.markdown.parent().outerWidth() - ui.area.markdown.outerWidth()) / 2;
+ //for ipad or wider device
+ if (rightMargin >= 133) {
+ newbool = true;
+ var affixLeftMargin = (ui.toc.affix.outerWidth() - ui.toc.affix.width()) / 2;
+ var left = ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - affixLeftMargin;
+ ui.toc.affix.css('left', left + 'px');
+ } else {
+ newbool = false;
+ }
+ //toc scrollspy
+ ui.toc.toc.removeClass('scrollspy-body, scrollspy-view');
+ ui.toc.affix.removeClass('scrollspy-body, scrollspy-view');
+ if (currentMode != modeType.both && !newbool) {
+ ui.toc.toc.addClass('scrollspy-body');
+ ui.toc.affix.addClass('scrollspy-body');
+ } else {
+ ui.toc.toc.addClass('scrollspy-view');
+ ui.toc.affix.addClass('scrollspy-body');
+ }
+ if (newbool != enoughForAffixToc) {
+ enoughForAffixToc = newbool;
+ generateScrollspy();
+ }
+}
+
function showStatus(type, num) {
currentStatus = type;
var shortStatus = ui.toolbar.shortStatus;
@@ -461,12 +569,21 @@ function changeMode(type) {
}
if (currentMode != modeType.view && visibleLG) {
//editor.focus();
- editor.refresh();
+ //editor.refresh();
} else {
editor.getInputField().blur();
}
- if (changeMode != modeType.edit)
+ if (currentMode == modeType.edit || currentMode == modeType.both) {
+ ui.toolbar.uploadImage.fadeIn();
+ } else {
+ ui.toolbar.uploadImage.fadeOut();
+ }
+ if (currentMode != modeType.edit) {
+ $(document.body).css('background-color', 'white');
updateView();
+ } else {
+ $(document.body).css('background-color', ui.area.codemirror.css('background-color'));
+ }
restoreInfo();
windowResize();
@@ -489,10 +606,9 @@ function changeMode(type) {
}
//button actions
-var noteId = window.location.pathname.split('/')[1];
-var url = window.location.origin + '/' + noteId;
-//pretty
-ui.toolbar.pretty.attr("href", url + "/pretty");
+var url = window.location.pathname;
+//share
+ui.toolbar.share.attr("href", url + "/share");
//download
//markdown
ui.toolbar.download.markdown.click(function () {
@@ -534,6 +650,73 @@ ui.toolbar.import.dropbox.click(function () {
ui.toolbar.import.clipboard.click(function () {
//na
});
+//upload image
+ui.toolbar.uploadImage.bind('change', function (e) {
+ var files = e.target.files || e.dataTransfer.files;
+ e.dataTransfer = {};
+ e.dataTransfer.files = files;
+ inlineAttach.onDrop(e);
+});
+//toc
+ui.toc.dropdown.click(function (e) {
+ e.stopPropagation();
+});
+
+function scrollToTop() {
+ if (currentMode == modeType.both) {
+ if (editor.getScrollInfo().top != 0)
+ editor.scrollTo(0, 0);
+ else
+ ui.area.view.animate({
+ scrollTop: 0
+ }, 100, "linear");
+ } else {
+ $(document.body).animate({
+ scrollTop: 0
+ }, 100, "linear");
+ }
+}
+
+function scrollToBottom() {
+ if (currentMode == modeType.both) {
+ var scrollInfo = editor.getScrollInfo();
+ var scrollHeight = scrollInfo.height;
+ if (scrollInfo.top != scrollHeight)
+ editor.scrollTo(0, scrollHeight * 2);
+ else
+ ui.area.view.animate({
+ scrollTop: ui.area.view[0].scrollHeight
+ }, 100, "linear");
+ } else {
+ $(document.body).animate({
+ scrollTop: $(document.body)[0].scrollHeight
+ }, 100, "linear");
+ }
+}
+
+var enoughForAffixToc = true;
+
+//scrollspy
+function generateScrollspy() {
+ $(document.body).scrollspy({
+ target: '.scrollspy-body'
+ });
+ ui.area.view.scrollspy({
+ target: '.scrollspy-view'
+ });
+ $(document.body).scrollspy('refresh');
+ ui.area.view.scrollspy('refresh');
+ if (enoughForAffixToc) {
+ ui.toc.toc.hide();
+ ui.toc.affix.show();
+ } else {
+ ui.toc.affix.hide();
+ ui.toc.toc.show();
+ }
+ $(document.body).scroll();
+ ui.area.view.scroll();
+}
+
//fix for wrong autofocus
$('#clipboardModal').on('shown.bs.modal', function () {
$('#clipboardModal').blur();
@@ -549,6 +732,9 @@ $("#clipboardModalConfirm").click(function () {
$("#clipboardModalContent").html('');
}
});
+$('#refreshModalRefresh').click(function () {
+ location.reload(true);
+});
function parseToEditor(data) {
var parsed = toMarkdown(data);
@@ -585,19 +771,20 @@ function importFromUrl(url) {
}
function isValidURL(str) {
- var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
- '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
- '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
- '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
- '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
- '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
- if (!pattern.test(str)) {
- return false;
- } else {
- return true;
- }
+ var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
+ '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
+ '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
+ if (!pattern.test(str)) {
+ return false;
+ } else {
+ return true;
}
- //mode
+}
+
+//mode
ui.toolbar.mode.click(function () {
toggleMode();
});
@@ -613,18 +800,63 @@ ui.toolbar.view.click(function () {
ui.toolbar.both.click(function () {
changeMode(modeType.both);
});
+//permission
+//freely
+ui.infobar.permission.freely.click(function () {
+ updatePermission("freely");
+});
+//editable
+ui.infobar.permission.editable.click(function () {
+ updatePermission("editable");
+});
+//locked
+ui.infobar.permission.locked.click(function () {
+ updatePermission("locked");
+});
+
+function updatePermission(_permission) {
+ if (_permission != permission) {
+ socket.emit('permission', _permission);
+ }
+}
+
+function checkPermission() {
+ var label = null;
+ var title = null;
+ switch (permission) {
+ case "freely":
+ label = '<i class="fa fa-leaf"></i> Freely';
+ title = "Anyone can edit";
+ break;
+ case "editable":
+ label = '<i class="fa fa-pencil"></i> Editable';
+ title = "Signed people can edit";
+ break;
+ case "locked":
+ label = '<i class="fa fa-lock"></i> Locked';
+ title = "Only owner can edit";
+ break;
+ }
+ if (personalInfo.userid == owner) {
+ label += ' <i class="fa fa-caret-down"></i>';
+ ui.infobar.permission.label.removeClass('disabled');
+ } else {
+ ui.infobar.permission.label.addClass('disabled');
+ }
+ ui.infobar.permission.label.html(label).attr('title', title);
+}
//socket.io actions
var socket = io.connect();
//overwrite original event for checking login state
var on = socket.on;
socket.on = function () {
- if (!checkLoginStateChanged())
+ if (!checkLoginStateChanged() && !needRefresh)
on.apply(socket, arguments);
};
var emit = socket.emit;
socket.emit = function () {
- if (!checkLoginStateChanged())
+ if (!checkLoginStateChanged() && !needRefresh)
emit.apply(socket, arguments);
};
socket.on('info', function (data) {
@@ -653,17 +885,53 @@ socket.on('connect', function (data) {
});
socket.on('version', function (data) {
if (data != version)
- location.reload(true);
+ setNeedRefresh();
+});
+socket.on('check', function (data) {
+ if (data.id == socket.id) {
+ lastchangetime = data.updatetime;
+ lastchangeui = ui.infobar.lastchange;
+ updateLastChange();
+ return;
+ }
+ var currentHash = md5(LZString.compressToUTF16(editor.getValue()));
+ var hashMismatch = (currentHash != data.hash);
+ if (hashMismatch)
+ socket.emit('refresh');
+ else {
+ lastchangetime = data.updatetime;
+ lastchangeui = ui.infobar.lastchange;
+ updateLastChange();
+ }
});
+socket.on('permission', function (data) {
+ permission = data.permission;
+ checkPermission();
+});
+var otk = null;
+var owner = null;
+var permission = null;
socket.on('refresh', function (data) {
+ var currentHash = md5(LZString.compressToUTF16(editor.getValue()));
+ var hashMismatch = (currentHash != data.hash);
saveInfo();
- var body = data.body;
- body = LZString.decompressFromUTF16(body);
- if (body)
- editor.setValue(body);
- else
- editor.setValue("");
+ otk = data.otk;
+ owner = data.owner;
+ permission = data.permission;
+
+ if (hashMismatch) {
+ var body = data.body;
+ body = LZString.decompressFromUTF16(body);
+ if (body)
+ editor.setValue(body);
+ else
+ editor.setValue("");
+ }
+
+ lastchangetime = data.updatetime;
+ lastchangeui = ui.infobar.lastchange;
+ updateLastChange();
if (!loaded) {
editor.clearHistory();
@@ -673,9 +941,19 @@ socket.on('refresh', function (data) {
loaded = true;
emitUserStatus(); //send first user status
updateOnlineStatus(); //update first online status
+ setTimeout(function () {
+ //work around editor not refresh
+ editor.refresh();
+ //work around cursor not refresh
+ for (var i = 0; i < onlineUsers.length; i++) {
+ buildCursor(onlineUsers[i]);
+ }
+ //work around might not scroll to hash
+ scrollToHash();
+ }, 1);
} else {
//if current doc is equal to the doc before disconnect
- if (LZString.compressToUTF16(editor.getValue()) !== data.body)
+ if (hashMismatch)
editor.clearHistory();
else {
if (lastInfo.history)
@@ -684,21 +962,58 @@ socket.on('refresh', function (data) {
lastInfo.history = null;
}
- updateView();
+ if (hashMismatch)
+ updateView();
if (editor.getOption('readOnly'))
editor.setOption('readOnly', false);
restoreInfo();
+ checkPermission();
});
+
+var changeStack = [];
+var changeBusy = false;
+
socket.on('change', function (data) {
data = LZString.decompressFromUTF16(data);
data = JSON.parse(data);
- editor.replaceRange(data.text, data.from, data.to, "ignoreHistory");
- isDirty = true;
- clearTimeout(finishChangeTimer);
- finishChangeTimer = setTimeout(finishChange, finishChangeDelay);
+ changeStack.push(data);
+ if (!changeBusy)
+ executeChange();
});
+
+function executeChange() {
+ if (changeStack.length > 0) {
+ changeBusy = true;
+ var data = changeStack.shift();
+ if (data.otk != otk) {
+ var found = false;
+ for (var i = 0, l = changeStack.length; i < l; i++) {
+ if (changeStack[i].otk == otk) {
+ changeStack.unshift(data);
+ data = changeStack[i];
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ socket.emit('refresh');
+ changeBusy = false;
+ return;
+ }
+ }
+ otk = data.nextotk;
+ if (data.id == personalInfo.id)
+ editor.replaceRange(data.text, data.from, data.to, 'self::' + data.origin);
+ else
+ editor.replaceRange(data.text, data.from, data.to, "ignoreHistory");
+ executeChange();
+ } else {
+ changeBusy = false;
+ }
+}
+
socket.on('online users', function (data) {
data = LZString.decompressFromUTF16(data);
data = JSON.parse(data);
@@ -795,7 +1110,7 @@ var onlineUserList = new List('online-user-list', options);
var shortOnlineUserList = new List('short-online-user-list', options);
function updateOnlineStatus() {
- if (!loaded) return;
+ if (!loaded || !socket.connected) return;
var _onlineUsers = deduplicateOnlineUsers(onlineUsers);
showStatus(statusType.online, _onlineUsers.length);
var items = onlineUserList.items;
@@ -851,7 +1166,7 @@ function sortOnlineUserList(list) {
var userbIsSelf = (userb.id == personalInfo.id || (userb.login && userb.userid == personalInfo.userid));
if (useraIsSelf && !userbIsSelf) {
return -1;
- } else if(!useraIsSelf && userbIsSelf) {
+ } else if (!useraIsSelf && userbIsSelf) {
return 1;
} else {
if (usera.login && !userb.login)
@@ -975,7 +1290,7 @@ function checkCursorTag(coord, ele) {
var offsetTop = defaultTextHeight;
if (width > 0 && height > 0) {
if (left + width + offsetLeft > editorWidth - curosrtagMargin) {
- offsetLeft = -(width + 4);
+ offsetLeft = -(width + 10);
}
if (top + height + offsetTop > Math.max(viewportHeight, editorHeight) + curosrtagMargin && top - height > curosrtagMargin) {
offsetTop = -(height);
@@ -1124,16 +1439,61 @@ function buildCursor(user) {
editor.on('beforeChange', function (cm, change) {
if (debug)
console.debug(change);
+ var self = change.origin.split('self::');
+ if (self.length == 2) {
+ change.origin = self[1];
+ self = true;
+ } else {
+ self = false;
+ }
+ if (self) {
+ change.canceled = true;
+ } else {
+ var isIgnoreEmitEvent = (ignoreEmitEvents.indexOf(change.origin) != -1);
+ if (!isIgnoreEmitEvent) {
+ switch (permission) {
+ case "freely":
+ //na
+ break;
+ case "editable":
+ if (!personalInfo.login) {
+ change.canceled = true;
+ $('.signin-modal').modal('show');
+ }
+ break;
+ case "locked":
+ if (personalInfo.userid != owner) {
+ change.canceled = true;
+ $('.locked-modal').modal('show');
+ }
+ break;
+ }
+ }
+ }
});
+
+var ignoreEmitEvents = ['setValue', 'ignoreHistory'];
editor.on('change', function (i, op) {
if (debug)
console.debug(op);
- if (op.origin != 'setValue' && op.origin != 'ignoreHistory') {
- socket.emit('change', LZString.compressToUTF16(JSON.stringify(op)));
+ var isIgnoreEmitEvent = (ignoreEmitEvents.indexOf(op.origin) != -1);
+ if (!isIgnoreEmitEvent) {
+ var out = {
+ text: op.text,
+ from: op.from,
+ to: op.to,
+ origin: op.origin
+ };
+ socket.emit('change', LZString.compressToUTF16(JSON.stringify(out)));
}
isDirty = true;
- clearTimeout(doneTypingTimer);
- doneTypingTimer = setTimeout(doneTyping, doneTypingDelay);
+ clearTimeout(finishChangeTimer);
+ finishChangeTimer = setTimeout(function () {
+ if (!isIgnoreEmitEvent)
+ finishChange(true);
+ else
+ finishChange(false);
+ }, finishChangeDelay);
});
editor.on('focus', function (cm) {
for (var i = 0; i < onlineUsers.length; i++) {
@@ -1236,22 +1596,17 @@ function restoreInfo() {
}
//view actions
-var doneTypingTimer = null;
var finishChangeTimer = null;
-var input = editor.getInputField();
-//user is "finished typing," do something
-function doneTyping() {
- emitRefresh();
- updateView();
-}
-function finishChange() {
+function finishChange(emit) {
+ if (emit)
+ emitUpdate();
updateView();
}
-function emitRefresh() {
+function emitUpdate() {
var value = editor.getValue();
- socket.emit('refresh', LZString.compressToUTF16(value));
+ socket.emit('update', LZString.compressToUTF16(value));
}
var lastResult = null;
@@ -1267,6 +1622,11 @@ function updateView() {
updateDataAttrs(result, ui.area.markdown.children().toArray());
lastResult = $(result).clone();
finishView(ui.area.view);
+ autoLinkify(ui.area.view);
+ generateToc('toc');
+ generateToc('toc-affix');
+ generateScrollspy();
+ smoothHashScroll();
writeHistory(ui.area.markdown);
isDirty = false;
clearMap();
@@ -1535,7 +1895,7 @@ $(editor.getInputField())
return '$1```' + lang + '=\n\n```';
},
done: function () {
- editor.doc.cm.moveV(-1, "line");
+ editor.doc.cm.execCommand("goLineUp");
},
context: function () {
return isInCode;
@@ -1556,12 +1916,19 @@ $(editor.getInputField())
return !isInCode;
}
},
- { //referral
- match: /(^|\n|\s)(\!|\!|\[\])(\w*)$/,
+ { //blockquote personal info & general info
+ match: /(?:^|\n|\s)(\>.*)(\[\])(\w*)$/,
search: function (term, callback) {
- callback($.map(supportReferrals, function (referral) {
- return referral.search.indexOf(term) === 0 ? referral.text : null;
- }));
+ var list = [];
+ $.map(supportBlockquoteTags, function (blockquotetag) {
+ if (blockquotetag.search.indexOf(term) === 0)
+ list.push(blockquotetag.command());
+ });
+ $.map(supportReferrals, function (referral) {
+ if (referral.search.indexOf(term) === 0)
+ list.push(referral.text);
+ })
+ callback(list);
checkCursorMenu();
},
replace: function (value) {
@@ -1571,11 +1938,11 @@ $(editor.getInputField())
return !isInCode;
}
},
- { //externals
- match: /(^|\n|\s)\{\}(\w*)$/,
+ { //referral
+ match: /(^|\n|\s)(\!|\!|\[\])(\w*)$/,
search: function (term, callback) {
- callback($.map(supportExternals, function (external) {
- return external.search.indexOf(term) === 0 ? external.text : null;
+ callback($.map(supportReferrals, function (referral) {
+ return referral.search.indexOf(term) === 0 ? referral.text : null;
}));
checkCursorMenu();
},
@@ -1586,36 +1953,16 @@ $(editor.getInputField())
return !isInCode;
}
},
- { //blockquote personal info & general info
- match: /(^|\n|\s|\>.*)\[(\w*)=$/,
- search: function (term, callback) {
- var list = typeof personalInfo[term] != 'undefined' ? [personalInfo[term]] : [];
- $.map(supportGenerals, function (general) {
- if (general.search.indexOf(term) === 0)
- list.push(general.command());
- });
- callback(list);
- checkCursorMenu();
- },
- replace: function (value) {
- return '$1[$2=' + value;
- },
- context: function (text) {
- return !isInCode;
- }
- },
- { //blockquote quick start tag
- match: /(^.*(?!>)\n|)(\>\s{0,1})$/,
+ { //externals
+ match: /(^|\n|\s)\{\}(\w*)$/,
search: function (term, callback) {
- var self = '[name=' + personalInfo.name + '] [time=' + moment().format('llll') + '] [color=' + personalInfo.color + ']';
- callback([self]);
+ callback($.map(supportExternals, function (external) {
+ return external.search.indexOf(term) === 0 ? external.text : null;
+ }));
checkCursorMenu();
},
- template: function (value) {
- return '[Your name, time, color tags]';
- },
replace: function (value) {
- return '$1$2' + value;
+ return '$1' + value;
},
context: function (text) {
return !isInCode;
diff --git a/public/js/pretty.js b/public/js/pretty.js
index 33b97803..6fff4d03 100644
--- a/public/js/pretty.js
+++ b/public/js/pretty.js
@@ -1,9 +1,81 @@
-var raw = $(".markdown-body").text();
-var markdown = LZString.decompressFromBase64(raw);
-var result = postProcess(md.render(markdown));
var markdown = $(".markdown-body");
-markdown.html(result);
-markdown.show();
+var text = $('<textarea/>').html(markdown.html()).text();
+var result = postProcess(md.render(text));
+markdown.html(result.html());
+$(document.body).show();
finishView(markdown);
autoLinkify(markdown);
-scrollToHash(); \ No newline at end of file
+generateToc('toc');
+generateToc('toc-affix');
+smoothHashScroll();
+lastchangetime = $('.ui-lastchange').text();
+lastchangeui = $('.ui-lastchange');
+updateLastChange();
+var url = window.location.pathname;
+$('.ui-edit').attr('href', url + '/edit');
+var toc = $('.ui-toc');
+var tocAffix = $('.ui-affix-toc');
+var tocDropdown = $('.ui-toc-dropdown');
+//toc
+tocDropdown.click(function (e) {
+ e.stopPropagation();
+});
+
+var enoughForAffixToc = true;
+
+function generateScrollspy() {
+ $(document.body).scrollspy({
+ target: ''
+ });
+ $(document.body).scrollspy('refresh');
+ if (enoughForAffixToc) {
+ toc.hide();
+ tocAffix.show();
+ } else {
+ tocAffix.hide();
+ toc.show();
+ }
+ $(document.body).scroll();
+}
+
+function windowResize() {
+ //toc right
+ var paddingRight = parseFloat(markdown.css('padding-right'));
+ var right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
+ toc.css('right', right + 'px');
+ //affix toc left
+ var newbool;
+ var rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
+ //for ipad or wider device
+ if (rightMargin >= 133) {
+ newbool = true;
+ var affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
+ var left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
+ tocAffix.css('left', left + 'px');
+ } else {
+ newbool = false;
+ }
+ if (newbool != enoughForAffixToc) {
+ enoughForAffixToc = newbool;
+ generateScrollspy();
+ }
+}
+$(window).resize(function () {
+ windowResize();
+});
+$(document).ready(function () {
+ windowResize();
+ generateScrollspy();
+});
+
+function scrollToTop() {
+ $(document.body).animate({
+ scrollTop: 0
+ }, 100, "linear");
+}
+
+function scrollToBottom() {
+ $(document.body).animate({
+ scrollTop: $(document.body)[0].scrollHeight
+ }, 100, "linear");
+} \ No newline at end of file
diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js
index 4dee0996..48829a4a 100644
--- a/public/js/syncscroll.js
+++ b/public/js/syncscroll.js
@@ -8,7 +8,7 @@ md.renderer.rules.blockquote_open = function (tokens, idx /*, options, env */ )
if (tokens[idx].lines && tokens[idx].level === 0) {
var startline = tokens[idx].lines[0] + 1;
var endline = tokens[idx].lines[1];
- return '<blockquote class="part" data-startline="' + startline + '" data-endline="' + endline + '">\n';
+ return '<blockquote class="raw part" data-startline="' + startline + '" data-endline="' + endline + '">\n';
}
return '<blockquote>\n';
};
@@ -55,9 +55,9 @@ md.renderer.rules.paragraph_open = function (tokens, idx) {
if (tokens[idx].lines && tokens[idx].level === 0) {
var startline = tokens[idx].lines[0] + 1;
var endline = tokens[idx].lines[1];
- return '<p class="part" data-startline="' + startline + '" data-endline="' + endline + '">';
+ return tokens[idx].tight ? '' : '<p class="part" data-startline="' + startline + '" data-endline="' + endline + '">';
}
- return '';
+ return tokens[idx].tight ? '' : '<p>';
};
md.renderer.rules.heading_open = function (tokens, idx) {
@@ -106,7 +106,7 @@ md.renderer.rules.fence = function (tokens, idx, options, env, self) {
}
langName = Remarkable.utils.escapeHtml(Remarkable.utils.replaceEntities(Remarkable.utils.unescapeMd(fenceName)));
- langClass = ' class="' + langPrefix + langName + '"';
+ langClass = ' class="' + langPrefix + langName.replace('=', '') + ' hljs"';
}
if (options.highlight) {
@@ -193,7 +193,8 @@ function buildMapInner(syncBack) {
'line-height': textarea.css('line-height'),
'word-wrap': wrap.css('word-wrap'),
'white-space': wrap.css('white-space'),
- 'word-break': wrap.css('word-break')
+ 'word-break': wrap.css('word-break'),
+ 'tab-size': '38px'
}).appendTo('body');
offset = ui.area.view.scrollTop() - ui.area.view.offset().top;
@@ -313,7 +314,7 @@ function syncScrollToView(event, _lineNo) {
var textHeight = editor.defaultTextHeight();
lineNo = Math.floor(scrollInfo.top / textHeight);
//if reach bottom, then scroll to end
- if (scrollInfo.top + scrollInfo.clientHeight >= scrollInfo.height - defaultTextHeight) {
+ if (scrollInfo.height > scrollInfo.clientHeight && scrollInfo.top + scrollInfo.clientHeight >= scrollInfo.height - defaultTextHeight) {
posTo = ui.area.view[0].scrollHeight - ui.area.view.height();
} else {
topDiffPercent = (scrollInfo.top % textHeight) / textHeight;