From 4e64583a0b6175d2c9a6729ffde1472dd55d389c Mon Sep 17 00:00:00 2001 From: Wu Cheng-Han Date: Fri, 15 May 2015 12:58:13 +0800 Subject: Marked as 0.2.8 --- public/css/cover.css | 61 +- public/css/github-extract.css | 3 +- public/css/index.css | 6 +- public/css/site.css | 1 + public/index.html | 73 +- public/js/common.js | 53 + public/js/cover.js | 245 +- public/js/extra.js | 6 +- public/js/fb.js | 8 + public/js/ga.js | 14 - public/js/history.js | 187 +- public/js/index.js | 419 +- public/js/pretty.js | 9 + public/js/syncscroll.js | 327 ++ public/vendor/codemirror/addon/dialog/dialog.css | 4 + .../codemirror/addon/scroll/annotatescrollbar.js | 2 +- .../codemirror/addon/scroll/simplescrollbars.css | 3 +- .../codemirror/addon/scroll/simplescrollbars.js | 3 +- public/vendor/codemirror/addon/search/search.js | 8 +- public/vendor/codemirror/codemirror.min.js | 18 +- public/vendor/codemirror/compress.sh | 12 +- public/vendor/codemirror/keymap/sublime.js | 12 +- public/vendor/gist-embed.js | 6 +- .../codemirror.inline-attachment.js | 95 + .../vendor/inlineAttachment/inline-attachment.js | 435 ++ public/vendor/jquery.cookie-1.4.1.min.js | 2 - public/vendor/js.cookie.js | 137 + public/vendor/select2/css/select2.css | 421 -- public/vendor/select2/css/select2.min.css | 1 - public/vendor/select2/js/i18n/az.js | 1 - public/vendor/select2/js/i18n/bg.js | 1 - public/vendor/select2/js/i18n/ca.js | 1 - public/vendor/select2/js/i18n/cs.js | 1 - public/vendor/select2/js/i18n/da.js | 1 - public/vendor/select2/js/i18n/de.js | 1 - public/vendor/select2/js/i18n/en.js | 1 - public/vendor/select2/js/i18n/es.js | 1 - public/vendor/select2/js/i18n/et.js | 1 - public/vendor/select2/js/i18n/eu.js | 1 - public/vendor/select2/js/i18n/fi.js | 1 - public/vendor/select2/js/i18n/fr.js | 1 - public/vendor/select2/js/i18n/gl.js | 1 - public/vendor/select2/js/i18n/hi.js | 1 - public/vendor/select2/js/i18n/hr.js | 1 - public/vendor/select2/js/i18n/hu.js | 1 - public/vendor/select2/js/i18n/id.js | 1 - public/vendor/select2/js/i18n/is.js | 1 - public/vendor/select2/js/i18n/it.js | 1 - public/vendor/select2/js/i18n/lt.js | 1 - public/vendor/select2/js/i18n/lv.js | 1 - public/vendor/select2/js/i18n/mk.js | 1 - public/vendor/select2/js/i18n/nb.js | 1 - public/vendor/select2/js/i18n/nl.js | 1 - public/vendor/select2/js/i18n/pl.js | 1 - public/vendor/select2/js/i18n/pt-BR.js | 1 - public/vendor/select2/js/i18n/pt.js | 1 - public/vendor/select2/js/i18n/ro.js | 1 - public/vendor/select2/js/i18n/ru.js | 1 - public/vendor/select2/js/i18n/sk.js | 1 - public/vendor/select2/js/i18n/sr.js | 1 - public/vendor/select2/js/i18n/sv.js | 1 - public/vendor/select2/js/i18n/th.js | 1 - public/vendor/select2/js/i18n/tr.js | 1 - public/vendor/select2/js/i18n/uk.js | 1 - public/vendor/select2/js/i18n/vi.js | 1 - public/vendor/select2/js/i18n/zh-CN.js | 1 - public/vendor/select2/js/i18n/zh-TW.js | 1 - public/vendor/select2/js/select2.amd.full.js | 5326 ------------------ public/vendor/select2/js/select2.amd.js | 4766 ---------------- public/vendor/select2/js/select2.full.js | 5765 -------------------- public/vendor/select2/js/select2.full.min.js | 3 - public/vendor/select2/js/select2.js | 5205 ------------------ public/vendor/select2/js/select2.min.js | 2 - public/vendor/select2/select2-bootstrap.css | 495 ++ public/vendor/select2/select2-spinner.gif | Bin 0 -> 1849 bytes public/vendor/select2/select2.css | 704 +++ public/vendor/select2/select2.min.js | 23 + public/vendor/select2/select2.png | Bin 0 -> 613 bytes public/vendor/select2/select2x2.png | Bin 0 -> 845 bytes public/vendor/store.min.js | 2 + public/vendor/url.min.js | 1 + public/views/foot.ejs | 53 +- public/views/head.ejs | 16 +- public/views/header.ejs | 4 +- public/views/pretty.ejs | 43 +- 85 files changed, 2977 insertions(+), 22040 deletions(-) create mode 100644 public/js/common.js create mode 100644 public/js/fb.js delete mode 100644 public/js/ga.js create mode 100644 public/js/pretty.js create mode 100644 public/js/syncscroll.js create mode 100755 public/vendor/inlineAttachment/codemirror.inline-attachment.js create mode 100755 public/vendor/inlineAttachment/inline-attachment.js delete mode 100644 public/vendor/jquery.cookie-1.4.1.min.js create mode 100755 public/vendor/js.cookie.js delete mode 100755 public/vendor/select2/css/select2.css delete mode 100755 public/vendor/select2/css/select2.min.css delete mode 100755 public/vendor/select2/js/i18n/az.js delete mode 100755 public/vendor/select2/js/i18n/bg.js delete mode 100755 public/vendor/select2/js/i18n/ca.js delete mode 100755 public/vendor/select2/js/i18n/cs.js delete mode 100755 public/vendor/select2/js/i18n/da.js delete mode 100755 public/vendor/select2/js/i18n/de.js delete mode 100755 public/vendor/select2/js/i18n/en.js delete mode 100755 public/vendor/select2/js/i18n/es.js delete mode 100755 public/vendor/select2/js/i18n/et.js delete mode 100755 public/vendor/select2/js/i18n/eu.js delete mode 100755 public/vendor/select2/js/i18n/fi.js delete mode 100755 public/vendor/select2/js/i18n/fr.js delete mode 100755 public/vendor/select2/js/i18n/gl.js delete mode 100755 public/vendor/select2/js/i18n/hi.js delete mode 100755 public/vendor/select2/js/i18n/hr.js delete mode 100755 public/vendor/select2/js/i18n/hu.js delete mode 100755 public/vendor/select2/js/i18n/id.js delete mode 100755 public/vendor/select2/js/i18n/is.js delete mode 100755 public/vendor/select2/js/i18n/it.js delete mode 100755 public/vendor/select2/js/i18n/lt.js delete mode 100755 public/vendor/select2/js/i18n/lv.js delete mode 100755 public/vendor/select2/js/i18n/mk.js delete mode 100755 public/vendor/select2/js/i18n/nb.js delete mode 100755 public/vendor/select2/js/i18n/nl.js delete mode 100755 public/vendor/select2/js/i18n/pl.js delete mode 100755 public/vendor/select2/js/i18n/pt-BR.js delete mode 100755 public/vendor/select2/js/i18n/pt.js delete mode 100755 public/vendor/select2/js/i18n/ro.js delete mode 100755 public/vendor/select2/js/i18n/ru.js delete mode 100755 public/vendor/select2/js/i18n/sk.js delete mode 100755 public/vendor/select2/js/i18n/sr.js delete mode 100755 public/vendor/select2/js/i18n/sv.js delete mode 100755 public/vendor/select2/js/i18n/th.js delete mode 100755 public/vendor/select2/js/i18n/tr.js delete mode 100755 public/vendor/select2/js/i18n/uk.js delete mode 100755 public/vendor/select2/js/i18n/vi.js delete mode 100755 public/vendor/select2/js/i18n/zh-CN.js delete mode 100755 public/vendor/select2/js/i18n/zh-TW.js delete mode 100755 public/vendor/select2/js/select2.amd.full.js delete mode 100755 public/vendor/select2/js/select2.amd.js delete mode 100755 public/vendor/select2/js/select2.full.js delete mode 100755 public/vendor/select2/js/select2.full.min.js delete mode 100755 public/vendor/select2/js/select2.js delete mode 100755 public/vendor/select2/js/select2.min.js create mode 100644 public/vendor/select2/select2-bootstrap.css create mode 100755 public/vendor/select2/select2-spinner.gif create mode 100755 public/vendor/select2/select2.css create mode 100755 public/vendor/select2/select2.min.js create mode 100755 public/vendor/select2/select2.png create mode 100755 public/vendor/select2/select2x2.png create mode 100644 public/vendor/store.min.js create mode 100755 public/vendor/url.min.js (limited to 'public') diff --git a/public/css/cover.css b/public/css/cover.css index da4207b8..30d54d34 100644 --- a/public/css/cover.css +++ b/public/css/cover.css @@ -23,13 +23,15 @@ a:hover { /* * Base structure */ - +html { + height: 100%; +} html, body { - height: 100%; background-color: #333; } body { + min-height: 100%; color: #fff; text-align: center; text-shadow: 0 1px 3px rgba(0, 0, 0, .5); @@ -40,7 +42,7 @@ body { padding: 10px; display: table; width: 100%; - height: 100%; + height: 100vh; /* For at least Firefox */ min-height: 100%; -webkit-box-shadow: inset 0 0 100px rgba(0, 0, 0, .5); @@ -156,7 +158,7 @@ body { .masthead, .mastfoot, .cover-container { - width: 700px; + width: 1000px; } } .section ul { @@ -168,41 +170,14 @@ html, body { overflow-x: hidden; } -.select2-selection, -.select2-search__field { - outline: 0; -} -.select2-search__field:hover { - border: 1px solid #b9b9b9 !important; - border-top-color: #a0a0a0 !important; -} -.select2-search__field:focus { - border: 1px solid #4d90fe !important; -} input { color: black; } .mastfoot { position: relative; } -.select2 { - width: 100% !important; - max-width: 400px; -} -.select2-selection { - height: 32px !important; -} -.select2-selection__rendered { - line-height: 32px !important; -} -.select2-selection__arrow { - height: 30px !important; -} -.select2-selection__rendered, -.select2-selection__placeholder, -.select2-results__option { - color: #000; - text-shadow: none; +.select2-container { + margin: 0 auto !important; } .list { width: 100%; @@ -275,4 +250,24 @@ input { .modal-title { text-align: left; color: black; +} + +.btn-file { + position: relative; + overflow: hidden; +} +.btn-file input[type=file] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + background: white; + cursor: inherit; + display: block; } \ No newline at end of file diff --git a/public/css/github-extract.css b/public/css/github-extract.css index 201aebc6..67ceb418 100644 --- a/public/css/github-extract.css +++ b/public/css/github-extract.css @@ -1,6 +1,5 @@ .markdown-body { overflow: hidden; - font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif; font-size: 16px; line-height: 1.6; word-wrap: break-word; @@ -339,7 +338,7 @@ } .task-list-item-checkbox { float: left; - margin: 0.4em 0 0.2em -1.3em !important; + margin: 0.31em 0 0.2em -1.3em !important; vertical-align: middle; cursor: default !important; } \ No newline at end of file diff --git a/public/css/index.css b/public/css/index.css index 6cfbeab0..b3a6bae5 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -20,7 +20,7 @@ form, font-family: 'Source Code Pro', Consolas, monaco, monospace; line-height: 18px; font-size: 16px; - height: auto; + /*height: auto;*/ min-height: 100%; overflow-y: hidden !important; -webkit-overflow-scrolling: touch; @@ -30,7 +30,7 @@ form, overflow-y: auto !important; } .CodeMirror-code { - padding-bottom: 100px; + /*padding-bottom: 72px;*/ } .CodeMirror-linenumber { opacity: 0.5; @@ -43,7 +43,7 @@ form, .CodeMirror-foldmarker { color: #d0d0d0; text-shadow: none; - font-family: arial; + font-family: Arial; line-height: .3; cursor: pointer; margin: 2px; diff --git a/public/css/site.css b/public/css/site.css index b50a1f9b..eed8b950 100644 --- a/public/css/site.css +++ b/public/css/site.css @@ -6,6 +6,7 @@ body { text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004); /*text-rendering: optimizeLegibility;*/ -webkit-overflow-scrolling: touch; + font-family: "Helvetica Neue", Helvetica, Arial, "Microsoft JhengHei", sans-serif !important; } :focus { outline: none !important; diff --git a/public/index.html b/public/index.html index ab844ab8..c52b8ec3 100644 --- a/public/index.html +++ b/public/index.html @@ -10,13 +10,6 @@ - - - - - - - HackMD - Collaborative notes @@ -24,7 +17,8 @@ - + + @@ -56,9 +50,7 @@

HackMD

- Realtime collaborate notes. -
Using markdown syntax. -
On all platforms. + Realtime collaborative markdown notes on all platforms.

@@ -76,12 +68,12 @@

Sign In to get own history!

-

Below are history from cookie

+

Below are history from browser


+
+ +
- - Sort by title + + Title - - Sort by time + + Time + +
-

+

- + - +
- - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/common.js b/public/js/common.js new file mode 100644 index 00000000..37591d36 --- /dev/null +++ b/public/js/common.js @@ -0,0 +1,53 @@ +//common +var domain = 'change this'; +var checkAuth = false; +var profile = null; +var lastLoginState = getLoginState(); +var loginStateChangeEvent = null; + +function resetCheckAuth() { + checkAuth = false; +} + +function setLoginState(bool) { + Cookies.set('loginstate', bool, { + expires: 14 + }); + if (loginStateChangeEvent && bool != lastLoginState) + loginStateChangeEvent(); + lastLoginState = bool; +} + +function getLoginState() { + return Cookies.get('loginstate') === "true"; +} + +function clearLoginState() { + Cookies.remove('loginstate'); +} + +function checkIfAuth(yesCallback, noCallback) { + var cookieLoginState = getLoginState(); + if (!checkAuth || typeof cookieLoginState == 'undefined') { + $.get('/me') + .done(function (data) { + if (data && data.status == 'ok') { + profile = data; + yesCallback(profile); + setLoginState(true); + } else { + noCallback(); + setLoginState(false); + } + }) + .fail(function () { + noCallback(); + setLoginState(false); + }); + checkAuth = true; + } else if (cookieLoginState) { + yesCallback(profile); + } else { + noCallback(); + } +} \ No newline at end of file diff --git a/public/js/cover.js b/public/js/cover.js index fea51f6a..24ba605c 100644 --- a/public/js/cover.js +++ b/public/js/cover.js @@ -1,3 +1,47 @@ +var options = { + valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags'], + item: '
  • \ + \ + \ +
    \ +
    \ +

    \ +

    \ +
    \ +

    \ +

    \ +
    \ +
    \ +
  • ' +}; +var historyList = new List('history', options); + +migrateHistoryFromTempCallback = pageInit; +loginStateChangeEvent = pageInit; +pageInit(); + +function pageInit() { + checkIfAuth( + function (data) { + $('.ui-signin').hide(); + $('.ui-or').hide(); + $('.ui-welcome').show(); + $('.ui-name').html(data.name); + $('.ui-signout').show(); + $(".ui-history").click(); + parseServerToHistory(historyList, parseHistoryCallback); + }, + function () { + $('.ui-signin').slideDown(); + $('.ui-or').slideDown(); + $('.ui-welcome').hide(); + $('.ui-name').html(''); + $('.ui-signout').hide(); + parseStorageToHistory(historyList, parseHistoryCallback); + } + ); +} + $(".masthead-nav li").click(function () { $(this).siblings().removeClass("active"); $(this).addClass("active"); @@ -19,63 +63,202 @@ $(".ui-releasenotes").click(function () { }); function checkHistoryList() { - if ($("#history-list").children().length > 0) + if ($("#history-list").children().length > 0) { $(".ui-nohistory").hide(); - else if ($("#history-list").children().length == 0) { + $(".ui-import-from-browser").hide(); + } else if ($("#history-list").children().length == 0) { $(".ui-nohistory").slideDown(); - var cookienotehistory = JSON.parse($.cookie('notehistory')); - if (login && cookienotehistory && cookienotehistory.length > 0) { - $(".ui-import-from-cookie").slideDown(); - } + getStorageHistory(function (data) { + if (data && data.length > 0 && getLoginState() && historyList.items.length == 0) { + $(".ui-import-from-browser").slideDown(); + } + }); } } -function parseHistoryCallback() { +function parseHistoryCallback(list, notehistory) { checkHistoryList(); + list.sort('timestamp', { + order: "desc" + }); + var filtertags = []; + $(".item").each(function (key, value) { + var a = $(this).closest("a"); + var id = a.siblings("span").html(); + var tagsEl = $(this).find(".tags"); + var item = historyList.get('id', id); + if (item.length > 0 && item[0]) { + var values = item[0].values(); + //parse link to element a + a.attr('href', '/' + values.id); + //parse tags + if (values.tags) { + var tags = values.tags; + if (tags.length > 0) { + var labels = []; + for (var j = 0; j < tags.length; j++) { + //push info filtertags if not found + var found = false; + if (filtertags.indexOf(tags[j]) != -1) + found = true; + if (!found) + filtertags.push(tags[j]); + //push into the item label + labels.push("" + tags[j] + ""); + } + tagsEl.html(labels.join(' ')); + } + } + } + }); $(".ui-history-close").click(function (e) { e.preventDefault(); - var id = $(this).closest("a").attr("href").split('/')[1]; + var id = $(this).closest("a").siblings("span").html(); getHistory(function (notehistory) { var newnotehistory = removeHistory(id, notehistory); saveHistory(newnotehistory); }); - $(this).closest("li").remove(); + list.remove('id', id); checkHistoryList(); }); + buildTagsFilter(filtertags); } -var login = false; +$(".ui-import-from-browser").click(function () { + saveStorageHistoryToServer(function () { + parseStorageToHistory(historyList, parseHistoryCallback); + }); +}); + +$(".ui-save-history").click(function () { + getHistory(function (data) { + var history = JSON.stringify(data); + var blob = new Blob([history], { + type: "application/json;charset=utf-8" + }); + saveAs(blob, 'hackmd_history_' + moment().format('YYYYMMDDHHmmss')); + }); +}); + +$(".ui-open-history").bind("change", function (e) { + var files = e.target.files || e.dataTransfer.files; + var file = files[0]; + var reader = new FileReader(); + reader.onload = function () { + var notehistory = JSON.parse(reader.result); + //console.log(notehistory); + if (!reader.result) return; + getHistory(function (data) { + var mergedata = data.concat(notehistory); + mergedata = clearDuplicatedHistory(mergedata); + saveHistory(mergedata); + parseHistory(historyList, parseHistoryCallback); + }); + $(".ui-open-history").replaceWith($(".ui-open-history").val('').clone(true)); + }; + reader.readAsText(file); +}); + +$(".ui-clear-history").click(function () { + saveHistory([]); + historyList.clear(); + checkHistoryList(); +}); + +$(".ui-refresh-history").click(function () { + resetCheckAuth(); + historyList.clear(); + parseHistory(historyList, parseHistoryCallback); +}); + +$(".ui-logout").click(function () { + clearLoginState(); + location.href = '/logout'; +}); -checkIfAuth( - function (data) { - $('.ui-signin').hide(); - $('.ui-or').hide(); - $('.ui-welcome').show(); - $('.ui-name').html(data.name); - $('.ui-signout').show(); - $(".ui-history").click(); - login = true; - }, - function () { - $('.ui-signin').slideDown(); - $('.ui-or').slideDown(); - login = false; +var filtertags = []; +$(".ui-use-tags").select2({ + placeholder: 'Use tags...', + multiple: true, + data: function () { + return { + results: filtertags + }; } -); +}); +$('.select2-input').css('width', 'inherit'); +buildTagsFilter([]); -parseHistory(parseHistoryCallback); +function buildTagsFilter(tags) { + for (var i = 0; i < tags.length; i++) + tags[i] = { + id: i, + text: tags[i] + }; + filtertags = tags; +} +$(".ui-use-tags").on('change', function () { + var tags = []; + var data = $(this).select2('data'); + for (var i = 0; i < data.length; i++) + tags.push(data[i].text); + if (tags.length > 0) { + historyList.filter(function (item) { + var values = item.values(); + if (!values.tags) return false; + var found = false; + for (var i = 0; i < tags.length; i++) { + if (values.tags.indexOf(tags[i]) != -1) { + found = true; + break; + } + } + return found; + }); + } else { + historyList.filter(); + } + checkHistoryList(); +}); -$(".ui-import-from-cookie").click(function () { - saveCookieHistoryToServer(function() { - parseCookieToHistory(parseHistoryCallback); - $(".ui-import-from-cookie").hide(); - }); +$('.search').keyup(function () { + checkHistoryList(); }); var source = $("#template").html(); var template = Handlebars.compile(source); var context = { release: [ + { + version: "0.2.8", + tag: "flame", + date: moment("201505151200", 'YYYYMMDDhhmm').fromNow(), + detail: [ + { + title: "Features", + item: [ + "+ Support drag-n-drop(exclude firefox) and paste image inline", + "+ Support tags filter in history", + "+ Support sublime-like shortcut keys" + ] + }, + { + title: "Enhancements", + item: [ + "* Adjust index description", + "* Adjust toolbar ui and view font", + "* Remove scroll sync delay and gain accuracy" + ] + }, + { + title: "Fixes", + item: [ + "* Partial update in the front and the end might not render properly", + "* Server not handle some editor events" + ] + } + ] + }, { version: "0.2.7", tag: "fuel", diff --git a/public/js/extra.js b/public/js/extra.js index 45833c89..05fa4704 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -28,6 +28,8 @@ function renderFilename(view) { return filename; } +var viewAjaxCallback = null; + //dynamic event or object binding here function finishView(view) { //youtube @@ -42,7 +44,7 @@ function finishView(view) { .each(function (key, value) { $.ajax({ type: 'GET', - url: 'http://vimeo.com/api/v2/video/' + $(value).attr('videoid') + '.json', + url: '//vimeo.com/api/v2/video/' + $(value).attr('videoid') + '.json', jsonp: 'callback', dataType: 'jsonp', success: function (data) { @@ -54,7 +56,7 @@ function finishView(view) { //gist view.find("code[data-gist-id]").each(function(key, value) { if($(value).children().length == 0) - $(value).gist(); + $(value).gist(viewAjaxCallback); }); //emojify emojify.run(view[0]); diff --git a/public/js/fb.js b/public/js/fb.js new file mode 100644 index 00000000..0bb7a466 --- /dev/null +++ b/public/js/fb.js @@ -0,0 +1,8 @@ +(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/ga.js b/public/js/ga.js deleted file mode 100644 index 0fda2410..00000000 --- a/public/js/ga.js +++ /dev/null @@ -1,14 +0,0 @@ -(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', 'get your self one', 'auto'); -ga('send', 'pageview'); \ No newline at end of file diff --git a/public/js/history.js b/public/js/history.js index c6deaa53..717a7ca4 100644 --- a/public/js/history.js +++ b/public/js/history.js @@ -1,16 +1,35 @@ -//common -function checkIfAuth(yesCallback, noCallback) { - $.get('/me') - .done(function (data) { - if (data && data.status == 'ok') { - yesCallback(data); - } else { - noCallback(); - } - }) - .fail(function () { - noCallback(); - }); +var migrateHistoryFromTempCallback = null; + +migrateHistoryFromTemp(); + +function migrateHistoryFromTemp() { + if (url('#tempid')) { + $.get('/temp', { + tempid: url('#tempid') + }) + .done(function (data) { + if (data && data.temp) { + getStorageHistory(function (olddata) { + if (!olddata || olddata.length == 0) { + saveHistoryToStorage(JSON.parse(data.temp)); + } + }); + } + }) + .always(function () { + var hash = location.hash.split('#')[1]; + hash = hash.split('&'); + for (var i = 0; i < hash.length; i++) + if (hash[i].indexOf('tempid') == 0) { + hash.splice(i, 1); + i--; + } + hash = hash.join('&'); + location.hash = hash; + if (migrateHistoryFromTempCallback) + migrateHistoryFromTempCallback(); + }); + } } function saveHistory(notehistory) { @@ -19,13 +38,20 @@ function saveHistory(notehistory) { saveHistoryToServer(notehistory); }, function () { - saveHistoryToCookie(notehistory); + saveHistoryToStorage(notehistory); } ); } +function saveHistoryToStorage(notehistory) { + if (store.enabled) + store.set('notehistory', JSON.stringify(notehistory)); + else + saveHistoryToCookie(notehistory); +} + function saveHistoryToCookie(notehistory) { - $.cookie('notehistory', JSON.stringify(notehistory), { + Cookies.set('notehistory', notehistory, { expires: 365 }); } @@ -36,12 +62,29 @@ function saveHistoryToServer(notehistory) { }); } +function saveCookieHistoryToStorage(callback) { + store.set('notehistory', Cookies.get('notehistory')); + callback(); +} + +function saveStorageHistoryToServer(callback) { + var data = store.get('notehistory'); + if (data) { + $.post('/history', { + history: data + }) + .done(function (data) { + callback(data); + }); + } +} + function saveCookieHistoryToServer(callback) { $.post('/history', { - history: $.cookie('notehistory') + history: Cookies.get('notehistory') }) .done(function (data) { - callback(); + callback(data); }); } @@ -58,7 +101,7 @@ function clearDuplicatedHistory(notehistory) { if (!found) newnotehistory.push(notehistory[i]); } - return notehistory; + return newnotehistory; } function addHistory(id, text, time, tags, notehistory) { @@ -86,7 +129,7 @@ function writeHistory(view) { writeHistoryToServer(view); }, function () { - writeHistoryToCookie(view); + writeHistoryToStorage(view); } ); } @@ -113,7 +156,7 @@ function writeHistoryToServer(view) { function writeHistoryToCookie(view) { try { - var notehistory = JSON.parse($.cookie('notehistory')); + var notehistory = Cookies.getJSON('notehistory'); } catch (err) { var notehistory = []; } @@ -122,6 +165,22 @@ function writeHistoryToCookie(view) { saveHistoryToCookie(newnotehistory); } +function writeHistoryToStorage(view) { + if (store.enabled) { + var data = store.get('notehistory'); + if (data) { + if (typeof data == "string") + data = JSON.parse(data); + var notehistory = data; + } else + var notehistory = []; + var newnotehistory = generateHistory(view, notehistory); + saveHistoryToStorage(newnotehistory); + } else { + writeHistoryToCookie(view); + } +} + function renderHistory(view) { var title = renderFilename(view); @@ -169,7 +228,7 @@ function getHistory(callback) { getServerHistory(callback); }, function () { - getCookieHistory(callback); + getStorageHistory(callback); } ); } @@ -187,70 +246,76 @@ function getServerHistory(callback) { } function getCookieHistory(callback) { - callback(JSON.parse($.cookie('notehistory'))); + callback(Cookies.getJSON('notehistory')); +} + +function getStorageHistory(callback) { + if (store.enabled) { + var data = store.get('notehistory'); + if (data) { + if (typeof data == "string") + data = JSON.parse(data); + callback(data); + } else + getCookieHistory(callback); + } else { + getCookieHistory(callback); + } } -function parseHistory(callback) { +function parseHistory(list, callback) { checkIfAuth( function () { - parseServerToHistory(callback); + parseServerToHistory(list, callback); }, function () { - parseCookieToHistory(callback); + parseStorageToHistory(list, callback); } ); } -function parseServerToHistory(callback) { +function parseServerToHistory(list, callback) { $.get('/history') .done(function (data) { if (data.history) { - //console.log(data.history); - parseToHistory(data.history, callback); + parseToHistory(list, data.history, callback); } }) .fail(function () { - parseCookieToHistory(callback); + parseCookieToHistory(list, callback); }); } -function parseCookieToHistory(callback) { - var notehistory = JSON.parse($.cookie('notehistory')); - parseToHistory(notehistory, callback); +function parseCookieToHistory(list, callback) { + var notehistory = Cookies.getJSON('notehistory'); + parseToHistory(list, notehistory, callback); } -function parseToHistory(notehistory, callback) { - if (notehistory && notehistory.length > 0) { - //console.log(notehistory); +function parseStorageToHistory(list, callback) { + if (store.enabled) { + var data = store.get('notehistory'); + if (data) { + if (typeof data == "string") + data = JSON.parse(data); + parseToHistory(list, data, callback); + } else + parseCookieToHistory(list, callback); + } else { + parseCookieToHistory(list, callback); + } +} + +function parseToHistory(list, notehistory, callback) { + if (!callback) return; + else if (!list || !notehistory) callback(list, notehistory); + 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].fromNow = moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a').fromNow(); + if (list.get('id', notehistory[i].id).length == 0) + list.add(notehistory[i]); } - $(notehistory).each(function (key, value) { - var close = "
    "; - var text = "

    " + value.text + "

    "; - var timestamp = ""; - var fromNow = " " + value.fromNow + ""; - var time = "" + value.time + ""; - var tags = ""; - if (value.tags) { - var labels = []; - for (var j = 0; j < value.tags.length; j++) - labels.push("" + value.tags[j] + ""); - tags = "

    " + labels.join(" ") + "

    "; - } - var li = "
  • " + close + text + '

    ' + fromNow + '
    ' + timestamp + time + '

    ' + tags + "
  • " - //console.debug(li); - $("#history-list").append(li); - }); } - - var options = { - valueNames: ['text', 'timestamp', 'fromNow', 'time', 'tags'] - }; - var historyList = new List('history', options); - historyList.sort('timestamp', { - order: "desc" - }); - callback(); + callback(list, notehistory); } \ No newline at end of file diff --git a/public/js/index.js b/public/js/index.js index 73b4e594..331251c9 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,12 +1,10 @@ //constant vars //settings -var debug = false; -var version = '0.2.7'; +var debug = true; +var version = '0.2.8'; var doneTypingDelay = 400; var finishChangeDelay = 400; var cursorActivityDelay = 50; -var syncScrollDelay = 50; -var scrollAnimatePeriod = 100; var cursorAnimatePeriod = 100; var modeType = { edit: {}, @@ -67,15 +65,20 @@ var lastInfo = { }; //editor settings -var editor = CodeMirror.fromTextArea(document.getElementById("textit"), { +var textit = document.getElementById("textit"); +if (!textit) throw new Error("There was no textit area!"); +var editor = CodeMirror.fromTextArea(textit, { mode: 'gfm', + keyMap: "sublime", viewportMargin: 20, styleActiveLine: true, lineNumbers: true, lineWrapping: true, + showCursorWhenSelecting: true, theme: "monokai", autofocus: true, inputStyle: "textarea", + scrollbarStyle: "overlay", matchBrackets: true, autoCloseBrackets: true, matchTags: { @@ -89,6 +92,7 @@ var editor = CodeMirror.fromTextArea(document.getElementById("textit"), { }, readOnly: true }); +inlineAttachment.editors.codemirror4.attach(editor); //ui vars var ui = { @@ -148,23 +152,33 @@ $(document).ready(function () { changeMode(currentMode); /* we need this only on touch devices */ if (isTouchDevice) { - /* cache dom references */ - var $body = jQuery('body'); + /* cache dom references */ + var $body = jQuery('body'); /* bind events */ $(document) - .on('focus', 'textarea, input', function() { - $body.addClass('fixfixed'); - }) - .on('blur', 'textarea, input', function() { - $body.removeClass('fixfixed'); - }); + .on('focus', 'textarea, input', function () { + $body.addClass('fixfixed'); + }) + .on('blur', 'textarea, input', function () { + $body.removeClass('fixfixed'); + }); } }); //when page resize +var windowResizeDelay = 200; +var windowResizeTimer = null; $(window).resize(function () { - checkResponsive(); + clearTimeout(windowResizeTimer); + windowResizeTimer = setTimeout(function () { + windowResize(); + }, windowResizeDelay); }); +function windowResize() { + checkResponsive(); + clearMap(); + syncScrollToView(); +} //768-792px have a gap function checkResponsive() { visibleXS = $(".visible-xs").is(":visible"); @@ -176,6 +190,10 @@ function checkResponsive() { changeMode(modeType.edit); else changeMode(modeType.view); + if (visibleXS) + $('.CodeMirror').css('height', 'auto'); + else + $('.CodeMirror').css('height', ''); } function showStatus(type, num) { @@ -220,7 +238,7 @@ function showStatus(type, num) { } function toggleMode() { - switch(currentMode) { + switch (currentMode) { case modeType.edit: changeMode(modeType.view); break; @@ -297,26 +315,31 @@ var url = window.location.origin + '/' + noteId; ui.toolbar.pretty.attr("href", url + "/pretty"); //download //markdown -ui.toolbar.download.markdown.click(function() { +ui.toolbar.download.markdown.click(function () { var filename = renderFilename(ui.area.markdown) + '.md'; var markdown = editor.getValue(); - var blob = new Blob([markdown], {type: "text/markdown;charset=utf-8"}); + var blob = new Blob([markdown], { + type: "text/markdown;charset=utf-8" + }); saveAs(blob, filename); }); //save to dropbox -ui.toolbar.save.dropbox.click(function() { +ui.toolbar.save.dropbox.click(function () { var filename = renderFilename(ui.area.markdown) + '.md'; var options = { files: [ - {'url': url + "/download", 'filename': filename} + { + 'url': url + "/download", + 'filename': filename + } ] }; Dropbox.save(options); }); //import from dropbox -ui.toolbar.import.dropbox.click(function() { +ui.toolbar.import.dropbox.click(function () { var options = { - success: function(files) { + success: function (files) { ui.spinner.show(); var url = files[0].link; importFromUrl(url); @@ -328,64 +351,73 @@ ui.toolbar.import.dropbox.click(function() { Dropbox.choose(options); }); //import from clipboard -ui.toolbar.import.clipboard.click(function() { +ui.toolbar.import.clipboard.click(function () { //na }); //fix for wrong autofocus -$('#clipboardModal').on('shown.bs.modal', function() { +$('#clipboardModal').on('shown.bs.modal', function () { $('#clipboardModal').blur(); }); -$("#clipboardModalClear").click(function() { +$("#clipboardModalClear").click(function () { $("#clipboardModalContent").html(''); }); -$("#clipboardModalConfirm").click(function() { +$("#clipboardModalConfirm").click(function () { var data = $("#clipboardModalContent").html(); - if(data) { + if (data) { parseToEditor(data); $('#clipboardModal').modal('hide'); $("#clipboardModalContent").html(''); } }); + function parseToEditor(data) { var parsed = toMarkdown(data); - if(parsed) - editor.replaceRange(parsed, {line:0, ch:0}, {line:editor.lastLine(), ch:editor.lastLine().length}, '+input'); + if (parsed) + editor.replaceRange(parsed, { + line: 0, + ch: 0 + }, { + line: editor.lastLine(), + ch: editor.lastLine().length + }, '+input'); } + function importFromUrl(url) { //console.log(url); - if(url == null) return; - if(!isValidURL(url)) { + if (url == null) return; + if (!isValidURL(url)) { alert('Not valid URL :('); return; } $.ajax({ method: "GET", url: url, - success: function(data) { + success: function (data) { parseToEditor(data); }, - error: function() { + error: function () { alert('Import failed :('); }, - complete: function() { + complete: function () { ui.spinner.hide(); } }); } + 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(); }); @@ -427,7 +459,7 @@ socket.on('version', function (data) { }); socket.on('refresh', function (data) { saveInfo(); - + var body = data.body; body = LZString.decompressFromBase64(body); if (body) @@ -455,7 +487,7 @@ socket.on('refresh', function (data) { if (editor.getOption('readOnly')) editor.setOption('readOnly', false); - + restoreInfo(); }); socket.on('change', function (data) { @@ -470,51 +502,65 @@ socket.on('online users', function (data) { if (debug) console.debug(data); showStatus(statusType.online, data.count); - $('.other-cursors').html(''); - for(var i = 0; i < data.users.length; i++) { + $('.other-cursors').children().each(function (key, value) { + var found = false; + for (var i = 0; i < data.users.length; i++) { + var user = data.users[i]; + if ($(this).attr('id') == user.id) + found = true; + } + if (!found) + $(this).remove(); + }); + for (var i = 0; i < data.users.length; i++) { var user = data.users[i]; - if(user.id != socket.id) + if (user.id != socket.id) buildCursor(user.id, user.color, user.cursor); } }); socket.on('cursor focus', function (data) { - if(debug) + if (debug) console.debug(data); var cursor = $('#' + data.id); - if(cursor.length > 0) { + if (cursor.length > 0) { cursor.fadeIn(); } else { - if(data.id != socket.id) + if (data.id != socket.id) buildCursor(data.id, data.color, data.cursor); } }); socket.on('cursor activity', function (data) { - if(debug) + if (debug) console.debug(data); - if(data.id != socket.id) + if (data.id != socket.id) buildCursor(data.id, data.color, data.cursor); }); socket.on('cursor blur', function (data) { - if(debug) + if (debug) console.debug(data); var cursor = $('#' + data.id); - if(cursor.length > 0) { + if (cursor.length > 0) { cursor.fadeOut(); } }); + function emitUserStatus() { checkIfAuth( function (data) { - socket.emit('user status', {login:true}); + socket.emit('user status', { + login: true + }); }, function () { - socket.emit('user status', {login:false}); + socket.emit('user status', { + login: false + }); } ); } function buildCursor(id, color, pos) { - if(!pos) return; + if (!pos) return; if ($('.other-cursors').length <= 0) { $("
    ").insertAfter('.CodeMirror-cursors'); } @@ -535,7 +581,10 @@ function buildCursor(id, color, pos) { cursor.attr('data-line', pos.line); cursor.attr('data-ch', pos.ch); var coord = editor.charCoords(pos, 'windows'); - cursor.stop(true).css('opacity', 1).animate({"left":coord.left, "top":coord.top}, cursorAnimatePeriod); + cursor.stop(true).css('opacity', 1).animate({ + "left": coord.left, + "top": coord.top + }, cursorAnimatePeriod); //cursor[0].style.left = coord.left + 'px'; //cursor[0].style.top = coord.top + 'px'; cursor[0].style.height = '18px'; @@ -566,6 +615,7 @@ editor.on('cursorActivity', function (cm) { clearTimeout(cursorActivityTimer); cursorActivityTimer = setTimeout(cursorActivity, cursorActivityDelay); }); + function cursorActivity() { socket.emit('cursor activity', editor.getCursor()); } @@ -578,8 +628,9 @@ function saveInfo() { var top = $(document.body).scrollTop(); switch (currentMode) { case modeType.edit: - lastInfo.edit.scroll.left = left; - lastInfo.edit.scroll.top = top; + //lastInfo.edit.scroll.left = left; + //lastInfo.edit.scroll.top = top; + lastInfo.edit.scroll = editor.getScrollInfo(); break; case modeType.view: lastInfo.view.scroll.left = left; @@ -603,8 +654,12 @@ function restoreInfo() { switch (currentMode) { case modeType.edit: - $(document.body).scrollLeft(lastInfo.edit.scroll.left); - $(document.body).scrollTop(lastInfo.edit.scroll.top); + //$(document.body).scrollLeft(lastInfo.edit.scroll.left); + //$(document.body).scrollTop(lastInfo.edit.scroll.top); + var left = lastInfo.edit.scroll.left; + var top = lastInfo.edit.scroll.top; + editor.scrollIntoView(); + editor.scrollTo(left, top); break; case modeType.view: $(document.body).scrollLeft(lastInfo.view.scroll.left); @@ -652,9 +707,8 @@ function updateView() { finishView(ui.area.view); writeHistory(ui.area.markdown); isDirty = false; - // reset lines mapping cache on content update - scrollMap = null; emitUserStatus(); + clearMap(); } function partialUpdate(src, tar, des) { @@ -702,7 +756,7 @@ function partialUpdate(src, tar, des) { } } //tar end - for (var i = 1; i <= tar.length; i++) { + for (var i = 1; i <= tar.length + 1; i++) { var srcLength = src.length; var tarLength = tar.length; copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline'); @@ -715,7 +769,7 @@ function partialUpdate(src, tar, des) { } } //src end - for (var i = 1; i <= src.length; i++) { + for (var i = 1; i <= src.length + 1; i++) { var srcLength = src.length; var tarLength = tar.length; copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline'); @@ -730,56 +784,75 @@ function partialUpdate(src, tar, des) { //check if tar end overlap tar start var overlap = 0; for (var i = start; i >= 0; i--) { - var rawTarStart = cloneAndRemoveDataAttr(tar[i-1]); - var rawTarEnd = cloneAndRemoveDataAttr(tar[tarEnd+1+start-i]); - if(rawTarStart && rawTarEnd && rawTarStart.outerHTML == rawTarEnd.outerHTML) + var rawTarStart = cloneAndRemoveDataAttr(tar[i - 1]); + var rawTarEnd = cloneAndRemoveDataAttr(tar[tarEnd + 1 + start - i]); + if (rawTarStart && rawTarEnd && rawTarStart.outerHTML == rawTarEnd.outerHTML) overlap++; else break; } - if(debug) + if (debug) console.log('overlap:' + overlap); //show diff content - if(debug) { + if (debug) { console.log('start:' + start); console.log('tarEnd:' + tarEnd); console.log('srcEnd:' + srcEnd); - console.log('des[start]:' + des[start]); } tarEnd += overlap; srcEnd += overlap; - //add new element - var newElements = ""; - for (var j = start; j <= srcEnd; j++) { - if(debug) - srcChanged += src[j].outerHTML; - newElements += src[j].outerHTML; + var repeatAdd = (start - srcEnd) < (start - tarEnd); + var repeatDiff = Math.abs(srcEnd - tarEnd) - 1; + //push new elements + var newElements = []; + if(srcEnd >= start) { + for (var j = start; j <= srcEnd; j++) { + if (!src[j]) continue; + newElements.push(src[j].outerHTML); + } + } else if(repeatAdd) { + for (var j = srcEnd - repeatDiff; j <= srcEnd; j++) { + if (!des[j]) continue; + newElements.push(des[j].outerHTML); + } } - if(newElements && des[start]) { - $(newElements).insertBefore(des[start]); - } else { - $(newElements).insertAfter(des[des.length-1]); + //push remove elements + var removeElements = []; + if(tarEnd >= start) { + for (var j = start; j <= tarEnd; j++) { + if (!des[j]) continue; + removeElements.push(des[j]); + } + } else if(!repeatAdd) { + for (var j = start; j <= start + repeatDiff; j++) { + if (!des[j]) continue; + removeElements.push(des[j]); + } } - if(debug) - console.log(srcChanged); - //remove old element - if(debug) - var tarChanged = ""; - for (var j = start; j <= tarEnd; j++) { - if(debug) - tarChanged += tar[j].outerHTML; - if(des[j]) - des[j].remove(); + //add elements + if (debug) { + console.log('ADD ELEMENTS'); + console.log(newElements.join('\n')); } - if(debug) { - console.log(tarChanged); - var srcChanged = ""; + if (des[start]) + $(newElements.join('')).insertBefore(des[start]); + else + $(newElements.join('')).insertAfter(des[start - 1]); + //remove elements + if (debug) + console.log('REMOVE ELEMENTS'); + for (var j = 0; j < removeElements.length; j++) { + if (debug) { + console.log(removeElements[j].outerHTML); + } + if (removeElements[j]) + removeElements[j].remove(); } } } function cloneAndRemoveDataAttr(el) { - if(!el) return; + if (!el) return; var rawEl = $(el).clone(true)[0]; rawEl.removeAttribute('data-startline'); rawEl.removeAttribute('data-endline'); @@ -789,152 +862,4 @@ function cloneAndRemoveDataAttr(el) { function copyAttribute(src, des, attr) { if (src && src.getAttribute(attr) && des) des.setAttribute(attr, src.getAttribute(attr)); -} - -// -// Inject line numbers for sync scroll. Notes: -// -// - We track only headings and paragraphs on first level. That's enougth. -// - Footnotes content causes jumps. Level limit filter it automatically. -// -md.renderer.rules.paragraph_open = function (tokens, idx) { - var line; - if (tokens[idx].lines && tokens[idx].level === 0) { - var startline = tokens[idx].lines[0] + 1; - var endline = tokens[idx].lines[1]; - return '

    '; - } - return ''; -}; - -md.renderer.rules.heading_open = function (tokens, idx) { - var line; - if (tokens[idx].lines && tokens[idx].level === 0) { - var startline = tokens[idx].lines[0] + 1; - var endline = tokens[idx].lines[1]; - return ''; - } - return ''; -}; - -editor.on('scroll', _.debounce(syncScrollToView, syncScrollDelay)); -//ui.area.view.on('scroll', _.debounce(syncScrollToEdit, 50)); -var scrollMap; -// Build offsets for each line (lines can be wrapped) -// That's a bit dirty to process each line everytime, but ok for demo. -// Optimizations are required only for big texts. -function buildScrollMap() { - var i, offset, nonEmptyList, pos, a, b, lineHeightMap, linesCount, - acc, sourceLikeDiv, textarea = ui.area.codemirror, - _scrollMap; - - sourceLikeDiv = $('

    ').css({ - position: 'absolute', - visibility: 'hidden', - height: 'auto', - width: editor.getScrollInfo().clientWidth, - 'font-size': textarea.css('font-size'), - 'font-family': textarea.css('font-family'), - 'line-height': textarea.css('line-height'), - 'white-space': textarea.css('white-space') - }).appendTo('body'); - - offset = ui.area.view.scrollTop() - ui.area.view.offset().top; - _scrollMap = []; - nonEmptyList = []; - lineHeightMap = []; - - acc = 0; - editor.getValue().split('\n').forEach(function (str) { - var h, lh; - - lineHeightMap.push(acc); - - if (str.length === 0) { - acc++; - return; - } - - sourceLikeDiv.text(str); - h = parseFloat(sourceLikeDiv.css('height')); - lh = parseFloat(sourceLikeDiv.css('line-height')); - acc += Math.round(h / lh); - }); - sourceLikeDiv.remove(); - lineHeightMap.push(acc); - linesCount = acc; - - for (i = 0; i < linesCount; i++) { - _scrollMap.push(-1); - } - - nonEmptyList.push(0); - _scrollMap[0] = 0; - - ui.area.markdown.find('.part').each(function (n, el) { - var $el = $(el), - t = $el.data('startline'); - if (t === '') { - return; - } - t = lineHeightMap[t]; - if (t !== 0) { - nonEmptyList.push(t); - } - _scrollMap[t] = Math.round($el.offset().top + offset); - }); - - nonEmptyList.push(linesCount); - _scrollMap[linesCount] = ui.area.view[0].scrollHeight; - - pos = 0; - for (i = 1; i < linesCount; i++) { - if (_scrollMap[i] !== -1) { - pos++; - continue; - } - - a = nonEmptyList[pos]; - b = nonEmptyList[pos + 1]; - _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a)); - } - - return _scrollMap; -} - -function syncScrollToView() { - var lineNo, posTo; - var scrollInfo = editor.getScrollInfo(); - if (!scrollMap) { - scrollMap = buildScrollMap(); - } - lineNo = Math.floor(scrollInfo.top / editor.defaultTextHeight()); - posTo = scrollMap[lineNo]; - ui.area.view.stop(true).animate({scrollTop: posTo}, scrollAnimatePeriod); -} - -function syncScrollToEdit() { - var lineNo, posTo; - if (!scrollMap) { - scrollMap = buildScrollMap(); - } - var top = ui.area.view.scrollTop(); - lineNo = closestIndex(top, scrollMap); - posTo = lineNo * editor.defaultTextHeight(); - editor.scrollTo(0, posTo); -} - -function closestIndex(num, arr) { - var curr = arr[0]; - var index = 0; - var diff = Math.abs(num - curr); - for (var val = 0; val < arr.length; val++) { - var newdiff = Math.abs(num - arr[val]); - if (newdiff < diff) { - diff = newdiff; - curr = arr[val]; - index = val; - } - } - return index; } \ No newline at end of file diff --git a/public/js/pretty.js b/public/js/pretty.js new file mode 100644 index 00000000..33b97803 --- /dev/null +++ b/public/js/pretty.js @@ -0,0 +1,9 @@ +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(); +finishView(markdown); +autoLinkify(markdown); +scrollToHash(); \ No newline at end of file diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js new file mode 100644 index 00000000..3d97324c --- /dev/null +++ b/public/js/syncscroll.js @@ -0,0 +1,327 @@ +// +// Inject line numbers for sync scroll. Notes: +// +// - We track only headings and paragraphs on first level. That's enougth. +// - Footnotes content causes jumps. Level limit filter it automatically. +// +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 '
    \n'; + } + return '
    \n'; +}; + +md.renderer.rules.table_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 '\n'; + } + return '
    \n'; +}; + +md.renderer.rules.bullet_list_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 '