summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
authorWu Cheng-Han2017-03-13 18:56:32 +0800
committerWu Cheng-Han2017-03-13 18:56:32 +0800
commitedb1b4aa0a72ac8b0215211c9dbc54156c3ff91f (patch)
treefb5f8ddbfe9001e266b3b2487c3b2e62fbd2bbb5 /public
parentc818cde78285490ec2931b68a72898f9754a6d81 (diff)
parent8246ac38506f8d62e2dd9699dcc4d62f14b65784 (diff)
Merge branch 'master' of https://github.com/jackycute/HackMD
Diffstat (limited to 'public')
-rw-r--r--public/js/cover.js739
-rw-r--r--public/js/extra.js1944
-rw-r--r--public/js/google-drive-picker.js227
-rw-r--r--public/js/google-drive-upload.js225
-rw-r--r--public/js/history.js500
-rw-r--r--public/js/htmlExport.js12
-rw-r--r--public/js/index.js7123
-rw-r--r--public/js/lib/common/login.js133
-rw-r--r--public/js/lib/config/index.js28
-rw-r--r--public/js/locale.js42
-rw-r--r--public/js/pretty.js211
-rw-r--r--public/js/render.js76
-rwxr-xr-xpublic/js/reveal-markdown.js741
-rw-r--r--public/js/slide.js195
-rw-r--r--public/js/syncscroll.js604
-rwxr-xr-xpublic/vendor/md-toc.js214
16 files changed, 6386 insertions, 6628 deletions
diff --git a/public/js/cover.js b/public/js/cover.js
index bc6e73f9..a45a1c13 100644
--- a/public/js/cover.js
+++ b/public/js/cover.js
@@ -1,7 +1,10 @@
-require('./locale');
+/* eslint-env browser, jquery */
+/* global moment, serverurl */
-require('../css/cover.css');
-require('../css/site.css');
+require('./locale')
+
+require('../css/cover.css')
+require('../css/site.css')
import {
checkIfAuth,
@@ -9,7 +12,7 @@ import {
getLoginState,
resetCheckAuth,
setloginStateChangeEvent
-} from './lib/common/login';
+} from './lib/common/login'
import {
clearDuplicatedHistory,
@@ -23,411 +26,403 @@ import {
removeHistory,
saveHistory,
saveStorageHistoryToServer
-} from './history';
+} from './history'
-import { saveAs } from 'file-saver';
-import List from 'list.js';
-import S from 'string';
+import { saveAs } from 'file-saver'
+import List from 'list.js'
+import S from 'string'
const options = {
- valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'],
- item: '<li class="col-xs-12 col-sm-6 col-md-6 col-lg-4">\
- <span class="id" style="display:none;"></span>\
- <a href="#">\
- <div class="item">\
- <div class="ui-history-pin fa fa-thumb-tack fa-fw"></div>\
- <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> visited </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>',
- page: 18,
- plugins: [
- ListPagination({
- outerWindow: 1
- })
- ]
-};
-const historyList = new List('history', options);
-
-migrateHistoryFromTempCallback = pageInit;
-setloginStateChangeEvent(pageInit);
-
-pageInit();
-
-function pageInit() {
- checkIfAuth(
+ valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'],
+ item: '<li class="col-xs-12 col-sm-6 col-md-6 col-lg-4">' +
+ '<span class="id" style="display:none;"></span>' +
+ '<a href="#">' +
+ '<div class="item">' +
+ '<div class="ui-history-pin fa fa-thumb-tack fa-fw"></div>' +
+ '<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> visited </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>',
+ page: 18,
+ plugins: [
+ window.ListPagination({
+ outerWindow: 1
+ })
+ ]
+}
+const historyList = new List('history', options)
+
+window.migrateHistoryFromTempCallback = pageInit
+setloginStateChangeEvent(pageInit)
+
+pageInit()
+
+function pageInit () {
+ checkIfAuth(
data => {
- $('.ui-signin').hide();
- $('.ui-or').hide();
- $('.ui-welcome').show();
- if (data.photo) $('.ui-avatar').prop('src', data.photo).show();
- else $('.ui-avatar').prop('src', '').hide();
- $('.ui-name').html(data.name);
- $('.ui-signout').show();
- $(".ui-history").click();
- parseServerToHistory(historyList, parseHistoryCallback);
+ $('.ui-signin').hide()
+ $('.ui-or').hide()
+ $('.ui-welcome').show()
+ if (data.photo) $('.ui-avatar').prop('src', data.photo).show()
+ else $('.ui-avatar').prop('src', '').hide()
+ $('.ui-name').html(data.name)
+ $('.ui-signout').show()
+ $('.ui-history').click()
+ parseServerToHistory(historyList, parseHistoryCallback)
},
() => {
- $('.ui-signin').show();
- $('.ui-or').show();
- $('.ui-welcome').hide();
- $('.ui-avatar').prop('src', '').hide();
- $('.ui-name').html('');
- $('.ui-signout').hide();
- parseStorageToHistory(historyList, parseHistoryCallback);
+ $('.ui-signin').show()
+ $('.ui-or').show()
+ $('.ui-welcome').hide()
+ $('.ui-avatar').prop('src', '').hide()
+ $('.ui-name').html('')
+ $('.ui-signout').hide()
+ parseStorageToHistory(historyList, parseHistoryCallback)
}
- );
+ )
}
-$(".masthead-nav li").click(function () {
- $(this).siblings().removeClass("active");
- $(this).addClass("active");
-});
+$('.masthead-nav li').click(function () {
+ $(this).siblings().removeClass('active')
+ $(this).addClass('active')
+})
// prevent empty link change hash
$('a[href="#"]').click(function (e) {
- e.preventDefault();
-});
-
-$(".ui-home").click(function (e) {
- if (!$("#home").is(':visible')) {
- $(".section:visible").hide();
- $("#home").fadeIn();
- }
-});
-
-$(".ui-history").click(() => {
- if (!$("#history").is(':visible')) {
- $(".section:visible").hide();
- $("#history").fadeIn();
- }
-});
-
-function checkHistoryList() {
- if ($("#history-list").children().length > 0) {
- $('.pagination').show();
- $(".ui-nohistory").hide();
- $(".ui-import-from-browser").hide();
- } else if ($("#history-list").children().length == 0) {
- $('.pagination').hide();
- $(".ui-nohistory").slideDown();
- getStorageHistory(data => {
- if (data && data.length > 0 && getLoginState() && historyList.items.length == 0) {
- $(".ui-import-from-browser").slideDown();
- }
- });
- }
+ e.preventDefault()
+})
+
+$('.ui-home').click(function (e) {
+ if (!$('#home').is(':visible')) {
+ $('.section:visible').hide()
+ $('#home').fadeIn()
+ }
+})
+
+$('.ui-history').click(() => {
+ if (!$('#history').is(':visible')) {
+ $('.section:visible').hide()
+ $('#history').fadeIn()
+ }
+})
+
+function checkHistoryList () {
+ if ($('#history-list').children().length > 0) {
+ $('.pagination').show()
+ $('.ui-nohistory').hide()
+ $('.ui-import-from-browser').hide()
+ } else if ($('#history-list').children().length === 0) {
+ $('.pagination').hide()
+ $('.ui-nohistory').slideDown()
+ getStorageHistory(data => {
+ if (data && data.length > 0 && getLoginState() && historyList.items.length === 0) {
+ $('.ui-import-from-browser').slideDown()
+ }
+ })
+ }
}
-function parseHistoryCallback(list, notehistory) {
- checkHistoryList();
- //sort by pinned then timestamp
- list.sort('', {
- sortFunction(a, b) {
- const notea = a.values();
- const noteb = b.values();
- if (notea.pinned && !noteb.pinned) {
- return -1;
- } else if (!notea.pinned && noteb.pinned) {
- return 1;
- } else {
- if (notea.timestamp > noteb.timestamp) {
- return -1;
- } else if (notea.timestamp < noteb.timestamp) {
- return 1;
- } else {
- return 0;
- }
- }
+function parseHistoryCallback (list, notehistory) {
+ checkHistoryList()
+ // sort by pinned then timestamp
+ list.sort('', {
+ sortFunction (a, b) {
+ const notea = a.values()
+ const noteb = b.values()
+ if (notea.pinned && !noteb.pinned) {
+ return -1
+ } else if (!notea.pinned && noteb.pinned) {
+ return 1
+ } else {
+ if (notea.timestamp > noteb.timestamp) {
+ return -1
+ } else if (notea.timestamp < noteb.timestamp) {
+ return 1
+ } else {
+ return 0
}
- });
+ }
+ }
+ })
// parse filter tags
- const filtertags = [];
- for (let i = 0, l = list.items.length; i < l; i++) {
- const tags = list.items[i]._values.tags;
- if (tags && tags.length > 0) {
- for (let j = 0; j < tags.length; j++) {
- //push info filtertags if not found
- let found = false;
- if (filtertags.includes(tags[j]))
- found = true;
- if (!found)
- filtertags.push(tags[j]);
- }
- }
+ const filtertags = []
+ for (let i = 0, l = list.items.length; i < l; i++) {
+ const tags = list.items[i]._values.tags
+ if (tags && tags.length > 0) {
+ for (let j = 0; j < tags.length; j++) {
+ // push info filtertags if not found
+ let found = false
+ if (filtertags.includes(tags[j])) { found = true }
+ if (!found) { filtertags.push(tags[j]) }
+ }
}
- buildTagsFilter(filtertags);
+ }
+ buildTagsFilter(filtertags)
}
// update items whenever list updated
historyList.on('updated', e => {
- for (let i = 0, l = e.items.length; i < l; i++) {
- const item = e.items[i];
- if (item.visible()) {
- const itemEl = $(item.elm);
- const values = item._values;
- const a = itemEl.find("a");
- const pin = itemEl.find(".ui-history-pin");
- const tagsEl = itemEl.find(".tags");
- //parse link to element a
- a.attr('href', `${serverurl}/${values.id}`);
- //parse pinned
- if (values.pinned) {
- pin.addClass('active');
- } else {
- pin.removeClass('active');
- }
- //parse tags
- const tags = values.tags;
- if (tags && tags.length > 0 && tagsEl.children().length <= 0) {
- const labels = [];
- for (let j = 0; j < tags.length; j++) {
- //push into the item label
- labels.push(`<span class='label label-default'>${tags[j]}</span>`);
- }
- tagsEl.html(labels.join(' '));
- }
+ for (let i = 0, l = e.items.length; i < l; i++) {
+ const item = e.items[i]
+ if (item.visible()) {
+ const itemEl = $(item.elm)
+ const values = item._values
+ const a = itemEl.find('a')
+ const pin = itemEl.find('.ui-history-pin')
+ const tagsEl = itemEl.find('.tags')
+ // parse link to element a
+ a.attr('href', `${serverurl}/${values.id}`)
+ // parse pinned
+ if (values.pinned) {
+ pin.addClass('active')
+ } else {
+ pin.removeClass('active')
+ }
+ // parse tags
+ const tags = values.tags
+ if (tags && tags.length > 0 && tagsEl.children().length <= 0) {
+ const labels = []
+ for (let j = 0; j < tags.length; j++) {
+ // push into the item label
+ labels.push(`<span class='label label-default'>${tags[j]}</span>`)
}
+ tagsEl.html(labels.join(' '))
+ }
}
- $(".ui-history-close").off('click');
- $(".ui-history-close").on('click', historyCloseClick);
- $(".ui-history-pin").off('click');
- $(".ui-history-pin").on('click', historyPinClick);
-});
-
-function historyCloseClick(e) {
- e.preventDefault();
- const id = $(this).closest("a").siblings("span").html();
- const value = historyList.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;
+ }
+ $('.ui-history-close').off('click')
+ $('.ui-history-close').on('click', historyCloseClick)
+ $('.ui-history-pin').off('click')
+ $('.ui-history-pin').on('click', historyPinClick)
+})
+
+function historyCloseClick (e) {
+ e.preventDefault()
+ const id = $(this).closest('a').siblings('span').html()
+ const value = historyList.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
}
-function historyPinClick(e) {
- e.preventDefault();
- const $this = $(this);
- const id = $this.closest("a").siblings("span").html();
- const item = historyList.get('id', id)[0];
- const values = item._values;
- let pinned = values.pinned;
- if (!values.pinned) {
- pinned = true;
- item._values.pinned = true;
- } else {
- pinned = false;
- item._values.pinned = false;
- }
- checkIfAuth(() => {
- postHistoryToServer(id, {
- pinned
- }, (err, result) => {
- if (!err) {
- if (pinned)
- $this.addClass('active');
- else
- $this.removeClass('active');
- }
- });
- }, () => {
- getHistory(notehistory => {
- for(let i = 0; i < notehistory.length; i++) {
- if (notehistory[i].id == id) {
- notehistory[i].pinned = pinned;
- break;
- }
- }
- saveHistory(notehistory);
- if (pinned)
- $this.addClass('active');
- else
- $this.removeClass('active');
- });
- });
+function historyPinClick (e) {
+ e.preventDefault()
+ const $this = $(this)
+ const id = $this.closest('a').siblings('span').html()
+ const item = historyList.get('id', id)[0]
+ const values = item._values
+ let pinned = values.pinned
+ if (!values.pinned) {
+ pinned = true
+ item._values.pinned = true
+ } else {
+ pinned = false
+ item._values.pinned = false
+ }
+ checkIfAuth(() => {
+ postHistoryToServer(id, {
+ pinned
+ }, (err, result) => {
+ if (!err) {
+ if (pinned) { $this.addClass('active') } else { $this.removeClass('active') }
+ }
+ })
+ }, () => {
+ getHistory(notehistory => {
+ for (let i = 0; i < notehistory.length; i++) {
+ if (notehistory[i].id === id) {
+ notehistory[i].pinned = pinned
+ break
+ }
+ }
+ saveHistory(notehistory)
+ if (pinned) { $this.addClass('active') } else { $this.removeClass('active') }
+ })
+ })
}
-//auto update item fromNow every minutes
-setInterval(updateItemFromNow, 60000);
+// auto update item fromNow every minutes
+setInterval(updateItemFromNow, 60000)
-function updateItemFromNow() {
- const items = $('.item').toArray();
- for (let i = 0; i < items.length; i++) {
- const item = $(items[i]);
- const timestamp = parseInt(item.find('.timestamp').text());
- item.find('.fromNow').text(moment(timestamp).fromNow());
- }
+function updateItemFromNow () {
+ const items = $('.item').toArray()
+ for (let i = 0; i < items.length; i++) {
+ const item = $(items[i])
+ const timestamp = parseInt(item.find('.timestamp').text())
+ item.find('.fromNow').text(moment(timestamp).fromNow())
+ }
}
-var clearHistory = false;
-var deleteId = null;
-
-function deleteHistory() {
- checkIfAuth(() => {
- deleteServerHistory(deleteId, (err, result) => {
- if (!err) {
- if (clearHistory) {
- historyList.clear();
- checkHistoryList();
- } else {
- historyList.remove('id', deleteId);
- checkHistoryList();
- }
- }
- $('.delete-modal').modal('hide');
- deleteId = null;
- clearHistory = false;
- });
- }, () => {
+var clearHistory = false
+var deleteId = null
+
+function deleteHistory () {
+ checkIfAuth(() => {
+ deleteServerHistory(deleteId, (err, result) => {
+ if (!err) {
if (clearHistory) {
- saveHistory([]);
- historyList.clear();
- checkHistoryList();
- deleteId = null;
+ historyList.clear()
+ checkHistoryList()
} else {
- if (!deleteId) return;
- getHistory(notehistory => {
- const newnotehistory = removeHistory(deleteId, notehistory);
- saveHistory(newnotehistory);
- historyList.remove('id', deleteId);
- checkHistoryList();
- deleteId = null;
- });
+ historyList.remove('id', deleteId)
+ checkHistoryList()
}
- $('.delete-modal').modal('hide');
- clearHistory = false;
- });
+ }
+ $('.delete-modal').modal('hide')
+ deleteId = null
+ clearHistory = false
+ })
+ }, () => {
+ if (clearHistory) {
+ saveHistory([])
+ historyList.clear()
+ checkHistoryList()
+ deleteId = null
+ } else {
+ if (!deleteId) return
+ getHistory(notehistory => {
+ const newnotehistory = removeHistory(deleteId, notehistory)
+ saveHistory(newnotehistory)
+ historyList.remove('id', deleteId)
+ checkHistoryList()
+ deleteId = null
+ })
+ }
+ $('.delete-modal').modal('hide')
+ clearHistory = false
+ })
}
-$(".ui-delete-modal-confirm").click(() => {
- deleteHistory();
-});
-
-$(".ui-import-from-browser").click(() => {
- saveStorageHistoryToServer(() => {
- parseStorageToHistory(historyList, parseHistoryCallback);
- });
-});
-
-$(".ui-save-history").click(() => {
+$('.ui-delete-modal-confirm').click(() => {
+ deleteHistory()
+})
+
+$('.ui-import-from-browser').click(() => {
+ saveStorageHistoryToServer(() => {
+ parseStorageToHistory(historyList, parseHistoryCallback)
+ })
+})
+
+$('.ui-save-history').click(() => {
+ getHistory(data => {
+ const history = JSON.stringify(data)
+ const blob = new Blob([history], {
+ type: 'application/json;charset=utf-8'
+ })
+ saveAs(blob, `hackmd_history_${moment().format('YYYYMMDDHHmmss')}`, true)
+ })
+})
+
+$('.ui-open-history').bind('change', e => {
+ const files = e.target.files || e.dataTransfer.files
+ const file = files[0]
+ const reader = new FileReader()
+ reader.onload = () => {
+ const notehistory = JSON.parse(reader.result)
+ // console.log(notehistory);
+ if (!reader.result) return
getHistory(data => {
- const history = JSON.stringify(data);
- const blob = new Blob([history], {
- type: "application/json;charset=utf-8"
- });
- saveAs(blob, `hackmd_history_${moment().format('YYYYMMDDHHmmss')}`, true);
- });
-});
-
-$(".ui-open-history").bind("change", e => {
- const files = e.target.files || e.dataTransfer.files;
- const file = files[0];
- const reader = new FileReader();
- reader.onload = () => {
- const notehistory = JSON.parse(reader.result);
- //console.log(notehistory);
- if (!reader.result) return;
- getHistory(data => {
- let 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(() => {
- $('.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(() => {
- const lastTags = $(".ui-use-tags").select2('val');
- $(".ui-use-tags").select2('val', '');
- historyList.filter();
- const lastKeyword = $('.search').val();
- $('.search').val('');
- historyList.search();
- $('#history-list').slideUp('fast');
- $('.pagination').hide();
-
- resetCheckAuth();
- historyList.clear();
- parseHistory(historyList, (list, notehistory) => {
- parseHistoryCallback(list, notehistory);
- $(".ui-use-tags").select2('val', lastTags);
- $(".ui-use-tags").trigger('change');
- historyList.search(lastKeyword);
- $('.search').val(lastKeyword);
- checkHistoryList();
- $('#history-list').slideDown('fast');
- });
-});
-
-$(".ui-logout").click(() => {
- clearLoginState();
- location.href = `${serverurl}/logout`;
-});
-
-let filtertags = [];
-$(".ui-use-tags").select2({
- placeholder: $(".ui-use-tags").attr('placeholder'),
- multiple: true,
- data() {
- return {
- results: filtertags
- };
+ let 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(() => {
+ $('.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(() => {
+ const lastTags = $('.ui-use-tags').select2('val')
+ $('.ui-use-tags').select2('val', '')
+ historyList.filter()
+ const lastKeyword = $('.search').val()
+ $('.search').val('')
+ historyList.search()
+ $('#history-list').slideUp('fast')
+ $('.pagination').hide()
+
+ resetCheckAuth()
+ historyList.clear()
+ parseHistory(historyList, (list, notehistory) => {
+ parseHistoryCallback(list, notehistory)
+ $('.ui-use-tags').select2('val', lastTags)
+ $('.ui-use-tags').trigger('change')
+ historyList.search(lastKeyword)
+ $('.search').val(lastKeyword)
+ checkHistoryList()
+ $('#history-list').slideDown('fast')
+ })
+})
+
+$('.ui-logout').click(() => {
+ clearLoginState()
+ location.href = `${serverurl}/logout`
+})
+
+let filtertags = []
+$('.ui-use-tags').select2({
+ placeholder: $('.ui-use-tags').attr('placeholder'),
+ multiple: true,
+ data () {
+ return {
+ results: filtertags
}
-});
-$('.select2-input').css('width', 'inherit');
-buildTagsFilter([]);
-
-function buildTagsFilter(tags) {
- for (let i = 0; i < tags.length; i++)
- tags[i] = {
- id: i,
- text: S(tags[i]).unescapeHTML().s
- };
- filtertags = tags;
-}
-$(".ui-use-tags").on('change', function () {
- const tags = [];
- const data = $(this).select2('data');
- for (let i = 0; i < data.length; i++)
- tags.push(data[i].text);
- if (tags.length > 0) {
- historyList.filter(item => {
- const values = item.values();
- if (!values.tags) return false;
- let found = false;
- for (let i = 0; i < tags.length; i++) {
- if (values.tags.includes(tags[i])) {
- found = true;
- break;
- }
- }
- return found;
- });
- } else {
- historyList.filter();
+ }
+})
+$('.select2-input').css('width', 'inherit')
+buildTagsFilter([])
+
+function buildTagsFilter (tags) {
+ for (let i = 0; i < tags.length; i++) {
+ tags[i] = {
+ id: i,
+ text: S(tags[i]).unescapeHTML().s
}
- checkHistoryList();
-});
+ }
+ filtertags = tags
+}
+$('.ui-use-tags').on('change', function () {
+ const tags = []
+ const data = $(this).select2('data')
+ for (let i = 0; i < data.length; i++) { tags.push(data[i].text) }
+ if (tags.length > 0) {
+ historyList.filter(item => {
+ const values = item.values()
+ if (!values.tags) return false
+ let found = false
+ for (let i = 0; i < tags.length; i++) {
+ if (values.tags.includes(tags[i])) {
+ found = true
+ break
+ }
+ }
+ return found
+ })
+ } else {
+ historyList.filter()
+ }
+ checkHistoryList()
+})
$('.search').keyup(() => {
- checkHistoryList();
-});
+ checkHistoryList()
+})
diff --git a/public/js/extra.js b/public/js/extra.js
index a3e840d2..844d52c6 100644
--- a/public/js/extra.js
+++ b/public/js/extra.js
@@ -1,1150 +1,1140 @@
-require('prismjs/themes/prism.css');
-require('prismjs/components/prism-wiki');
-require('prismjs/components/prism-haskell');
-require('prismjs/components/prism-go');
-require('prismjs/components/prism-typescript');
-require('prismjs/components/prism-jsx');
-
-import Prism from 'prismjs';
-import hljs from 'highlight.js';
-import PDFObject from 'pdfobject';
-import S from 'string';
-import { saveAs } from 'file-saver';
-
-require('./lib/common/login');
-require('../vendor/md-toc');
-var Viz = require("viz.js");
-
-//auto update last change
-window.createtime = null;
-window.lastchangetime = null;
+/* eslint-env browser, jquery */
+/* global moment, serverurl */
+
+require('prismjs/themes/prism.css')
+require('prismjs/components/prism-wiki')
+require('prismjs/components/prism-haskell')
+require('prismjs/components/prism-go')
+require('prismjs/components/prism-typescript')
+require('prismjs/components/prism-jsx')
+
+import Prism from 'prismjs'
+import hljs from 'highlight.js'
+import PDFObject from 'pdfobject'
+import S from 'string'
+import { saveAs } from 'file-saver'
+
+require('./lib/common/login')
+require('../vendor/md-toc')
+var Viz = require('viz.js')
+
+// auto update last change
+window.createtime = null
+window.lastchangetime = null
window.lastchangeui = {
- status: $(".ui-status-lastchange"),
- time: $(".ui-lastchange"),
- user: $(".ui-lastchangeuser"),
- nouser: $(".ui-no-lastchangeuser")
+ status: $('.ui-status-lastchange'),
+ time: $('.ui-lastchange'),
+ user: $('.ui-lastchangeuser'),
+ nouser: $('.ui-no-lastchangeuser')
}
-const ownerui = $(".ui-owner");
+const ownerui = $('.ui-owner')
-export function updateLastChange() {
- if (!lastchangeui) return;
- if (createtime) {
- if (createtime && !lastchangetime) {
- lastchangeui.status.text('created');
- } else {
- lastchangeui.status.text('changed');
- }
- const time = lastchangetime || createtime;
- lastchangeui.time.html(moment(time).fromNow());
- lastchangeui.time.attr('title', moment(time).format('llll'));
+export function updateLastChange () {
+ if (!window.lastchangeui) return
+ if (window.createtime) {
+ if (window.createtime && !window.lastchangetime) {
+ window.lastchangeui.status.text('created')
+ } else {
+ window.lastchangeui.status.text('changed')
}
+ const time = window.lastchangetime || window.createtime
+ window.lastchangeui.time.html(moment(time).fromNow())
+ window.lastchangeui.time.attr('title', moment(time).format('llll'))
+ }
}
-setInterval(updateLastChange, 60000);
-
-window.lastchangeuser = null;
-window.lastchangeuserprofile = null;
-
-export function updateLastChangeUser() {
- if (lastchangeui) {
- if (lastchangeuser && lastchangeuserprofile) {
- const icon = lastchangeui.user.children('i');
- icon.attr('title', lastchangeuserprofile.name).tooltip('fixTitle');
- if (lastchangeuserprofile.photo)
- icon.attr('style', `background-image:url(${lastchangeuserprofile.photo})`);
- lastchangeui.user.show();
- lastchangeui.nouser.hide();
- } else {
- lastchangeui.user.hide();
- lastchangeui.nouser.show();
- }
+setInterval(updateLastChange, 60000)
+
+window.lastchangeuser = null
+window.lastchangeuserprofile = null
+
+export function updateLastChangeUser () {
+ if (window.lastchangeui) {
+ if (window.lastchangeuser && window.lastchangeuserprofile) {
+ const icon = window.lastchangeui.user.children('i')
+ icon.attr('title', window.lastchangeuserprofile.name).tooltip('fixTitle')
+ if (window.lastchangeuserprofile.photo) { icon.attr('style', `background-image:url(${window.lastchangeuserprofile.photo})`) }
+ window.lastchangeui.user.show()
+ window.lastchangeui.nouser.hide()
+ } else {
+ window.lastchangeui.user.hide()
+ window.lastchangeui.nouser.show()
}
+ }
}
-window.owner = null;
-window.ownerprofile = null;
-
-export function updateOwner() {
- if (ownerui) {
- if (owner && ownerprofile && owner !== lastchangeuser) {
- const icon = ownerui.children('i');
- icon.attr('title', ownerprofile.name).tooltip('fixTitle');
- const styleString = `background-image:url(${ownerprofile.photo})`;
- if (ownerprofile.photo && icon.attr('style') !== styleString)
- icon.attr('style', styleString);
- ownerui.show();
- } else {
- ownerui.hide();
- }
+window.owner = null
+window.ownerprofile = null
+
+export function updateOwner () {
+ if (window.ownerui) {
+ if (window.owner && window.ownerprofile && window.owner !== window.lastchangeuser) {
+ const icon = ownerui.children('i')
+ icon.attr('title', window.ownerprofile.name).tooltip('fixTitle')
+ const styleString = `background-image:url(${window.ownerprofile.photo})`
+ if (window.ownerprofile.photo && icon.attr('style') !== styleString) { icon.attr('style', styleString) }
+ ownerui.show()
+ } else {
+ ownerui.hide()
}
+ }
}
-//get title
-function getTitle(view) {
- let title = "";
- if (md && md.meta && md.meta.title && (typeof md.meta.title == "string" || typeof md.meta.title == "number")) {
- title = md.meta.title;
+// get title
+function getTitle (view) {
+ let title = ''
+ if (md && md.meta && md.meta.title && (typeof md.meta.title === 'string' || typeof md.meta.title === 'number')) {
+ title = md.meta.title
+ } else {
+ const h1s = view.find('h1')
+ if (h1s.length > 0) {
+ title = h1s.first().text()
} else {
- const h1s = view.find("h1");
- if (h1s.length > 0) {
- title = h1s.first().text();
- } else {
- title = null;
- }
+ title = null
}
- return title;
+ }
+ return title
}
-//render title
-export function renderTitle(view) {
- let title = getTitle(view);
- if (title) {
- title += ' - HackMD';
- } else {
- title = 'HackMD - Collaborative markdown notes';
- }
- return title;
+// render title
+export function renderTitle (view) {
+ let title = getTitle(view)
+ if (title) {
+ title += ' - HackMD'
+ } else {
+ title = 'HackMD - Collaborative markdown notes'
+ }
+ return title
}
-//render filename
-export function renderFilename(view) {
- let filename = getTitle(view);
- if (!filename) {
- filename = 'Untitled';
- }
- return filename;
+// render filename
+export function renderFilename (view) {
+ let filename = getTitle(view)
+ if (!filename) {
+ filename = 'Untitled'
+ }
+ return filename
}
// render tags
-export function renderTags(view) {
- const tags = [];
- const rawtags = [];
- if (md && md.meta && md.meta.tags && (typeof md.meta.tags == "string" || typeof md.meta.tags == "number")) {
- const metaTags = (`${md.meta.tags}`).split(',');
- for (var i = 0; i < metaTags.length; i++) {
- const text = metaTags[i].trim();
- if (text) rawtags.push(text);
- }
- } else {
- view.find('h6').each((key, value) => {
- if (/^tags/gmi.test($(value).text())) {
- const codes = $(value).find("code");
- for (let i = 0; i < codes.length; i++) {
- const text = codes[i].innerHTML.trim();
- if (text) rawtags.push(text);
- }
- }
- });
+export function renderTags (view) {
+ const tags = []
+ const rawtags = []
+ if (md && md.meta && md.meta.tags && (typeof md.meta.tags === 'string' || typeof md.meta.tags === 'number')) {
+ const metaTags = (`${md.meta.tags}`).split(',')
+ for (let i = 0; i < metaTags.length; i++) {
+ const text = metaTags[i].trim()
+ if (text) rawtags.push(text)
}
- for (var i = 0; i < rawtags.length; i++) {
- let found = false;
- for (let j = 0; j < tags.length; j++) {
- if (tags[j] == rawtags[i]) {
- found = true;
- break;
- }
+ } else {
+ view.find('h6').each((key, value) => {
+ if (/^tags/gmi.test($(value).text())) {
+ const codes = $(value).find('code')
+ for (let i = 0; i < codes.length; i++) {
+ const text = codes[i].innerHTML.trim()
+ if (text) rawtags.push(text)
}
- if (!found)
- tags.push(rawtags[i]);
+ }
+ })
+ }
+ for (let i = 0; i < rawtags.length; i++) {
+ let found = false
+ for (let j = 0; j < tags.length; j++) {
+ if (tags[j] === rawtags[i]) {
+ found = true
+ break
+ }
}
- return tags;
+ if (!found) { tags.push(rawtags[i]) }
+ }
+ return tags
}
-function slugifyWithUTF8(text) {
- let newText = S(text.toLowerCase()).trim().stripTags().dasherize().s;
- newText = newText.replace(/([\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\[\\\]\^\`\{\|\}\~])/g, '');
- return newText;
+function slugifyWithUTF8 (text) {
+ let newText = S(text.toLowerCase()).trim().stripTags().dasherize().s
+ newText = newText.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '')
+ return newText
}
-export function isValidURL(str) {
- const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
+export function isValidURL (str) {
+ const 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;
- }
+ '(\\#[-a-z\\d_]*)?$', 'i') // fragment locator
+ if (!pattern.test(str)) {
+ return false
+ } else {
+ return true
+ }
}
-//parse meta
-export function parseMeta(md, edit, view, toc, tocAffix) {
- let lang = null;
- let dir = null;
- let breaks = true;
- if (md && md.meta) {
- const meta = md.meta;
- lang = meta.lang;
- dir = meta.dir;
- breaks = meta.breaks;
- }
- //text language
- if (lang && typeof lang == "string") {
- view.attr('lang', lang);
- toc.attr('lang', lang);
- tocAffix.attr('lang', lang);
- if (edit)
- edit.attr('lang', lang);
- } else {
- view.removeAttr('lang');
- toc.removeAttr('lang');
- tocAffix.removeAttr('lang');
- if (edit)
- edit.removeAttr('lang', lang);
- }
- //text direction
- if (dir && typeof dir == "string") {
- view.attr('dir', dir);
- toc.attr('dir', dir);
- tocAffix.attr('dir', dir);
- } else {
- view.removeAttr('dir');
- toc.removeAttr('dir');
- tocAffix.removeAttr('dir');
- }
- //breaks
- if (typeof breaks === 'boolean' && !breaks) {
- md.options.breaks = false;
- } else {
- md.options.breaks = true;
- }
+// parse meta
+export function parseMeta (md, edit, view, toc, tocAffix) {
+ let lang = null
+ let dir = null
+ let breaks = true
+ if (md && md.meta) {
+ const meta = md.meta
+ lang = meta.lang
+ dir = meta.dir
+ breaks = meta.breaks
+ }
+ // text language
+ if (lang && typeof lang === 'string') {
+ view.attr('lang', lang)
+ toc.attr('lang', lang)
+ tocAffix.attr('lang', lang)
+ if (edit) { edit.attr('lang', lang) }
+ } else {
+ view.removeAttr('lang')
+ toc.removeAttr('lang')
+ tocAffix.removeAttr('lang')
+ if (edit) { edit.removeAttr('lang', lang) }
+ }
+ // text direction
+ if (dir && typeof dir === 'string') {
+ view.attr('dir', dir)
+ toc.attr('dir', dir)
+ tocAffix.attr('dir', dir)
+ } else {
+ view.removeAttr('dir')
+ toc.removeAttr('dir')
+ tocAffix.removeAttr('dir')
+ }
+ // breaks
+ if (typeof breaks === 'boolean' && !breaks) {
+ md.options.breaks = false
+ } else {
+ md.options.breaks = true
+ }
}
-window.viewAjaxCallback = null;
-
-//regex for extra tags
-const spaceregex = /\s*/;
-const notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/;
-let coloregex = /\[color=([#|\(|\)|\s|\,|\w]*?)\]/;
-coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, "g");
-let nameregex = /\[name=(.*?)\]/;
-let timeregex = /\[time=([:|,|+|-|\(|\)|\s|\w]*?)\]/;
-const nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, "g");
-nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, "g");
-timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, "g");
-
-function replaceExtraTags(html) {
- html = html.replace(coloregex, '<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>');
- return html;
+window.viewAjaxCallback = null
+
+// regex for extra tags
+const spaceregex = /\s*/
+const notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/
+let coloregex = /\[color=([#|(|)|\s|,|\w]*?)\]/
+coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, 'g')
+let nameregex = /\[name=(.*?)\]/
+let timeregex = /\[time=([:|,|+|-|(|)|\s|\w]*?)\]/
+const nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, 'g')
+nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, 'g')
+timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, 'g')
+
+function replaceExtraTags (html) {
+ html = html.replace(coloregex, '<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>')
+ return html
}
-if (typeof mermaid !== 'undefined' && mermaid) mermaid.startOnLoad = false;
+if (typeof window.mermaid !== 'undefined' && window.mermaid) window.mermaid.startOnLoad = false
-//dynamic event or object binding here
-export function finishView(view) {
- //todo list
- const lis = view.find('li.raw').removeClass("raw").sortByDepth().toArray();
+// dynamic event or object binding here
+export function finishView (view) {
+ // todo list
+ const lis = view.find('li.raw').removeClass('raw').sortByDepth().toArray()
- for (let li of lis) {
- let html = $(li).clone()[0].innerHTML;
- const p = $(li).children('p');
- if (p.length == 1) {
- html = p.html();
- li = p[0];
- }
- html = replaceExtraTags(html);
- li.innerHTML = html;
- let disabled = 'disabled';
- if(typeof editor !== 'undefined' && havePermission())
- disabled = '';
- 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>`);
- li.setAttribute('class', 'task-list-item');
- }
- if (typeof editor !== 'undefined' && havePermission())
- $(li).find('input').change(toggleTodoEvent);
- //color tag in list will convert it to tag icon with color
- const tag_color = $(li).closest('ul').find(".color");
- tag_color.each((key, value) => {
- $(value).addClass('fa fa-tag').css('color', $(value).attr('data-color'));
- });
+ for (let li of lis) {
+ let html = $(li).clone()[0].innerHTML
+ const p = $(li).children('p')
+ if (p.length === 1) {
+ html = p.html()
+ li = p[0]
}
-
- //youtube
- view.find("div.youtube.raw").removeClass("raw")
+ html = replaceExtraTags(html)
+ li.innerHTML = html
+ let disabled = 'disabled'
+ if (typeof editor !== 'undefined' && window.havePermission()) { disabled = '' }
+ 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>`)
+ li.setAttribute('class', 'task-list-item')
+ }
+ if (typeof editor !== 'undefined' && window.havePermission()) { $(li).find('input').change(toggleTodoEvent) }
+ // color tag in list will convert it to tag icon with color
+ const tagColor = $(li).closest('ul').find('.color')
+ tagColor.each((key, value) => {
+ $(value).addClass('fa fa-tag').css('color', $(value).attr('data-color'))
+ })
+ }
+
+ // youtube
+ view.find('div.youtube.raw').removeClass('raw')
.click(function () {
- imgPlayiframe(this, '//www.youtube.com/embed/');
- });
- //vimeo
- view.find("div.vimeo.raw").removeClass("raw")
+ imgPlayiframe(this, '//www.youtube.com/embed/')
+ })
+ // vimeo
+ view.find('div.vimeo.raw').removeClass('raw')
.click(function () {
- imgPlayiframe(this, '//player.vimeo.com/video/');
+ imgPlayiframe(this, '//player.vimeo.com/video/')
})
.each((key, value) => {
- $.ajax({
- type: 'GET',
- url: `//vimeo.com/api/v2/video/${$(value).attr('data-videoid')}.json`,
- jsonp: 'callback',
- dataType: 'jsonp',
- success(data) {
- const thumbnail_src = data[0].thumbnail_large;
- const image = `<img src="${thumbnail_src}" />`;
- $(value).prepend(image);
- if(viewAjaxCallback) viewAjaxCallback();
- }
- });
- });
- //gist
- view.find("code[data-gist-id]").each((key, value) => {
- if ($(value).children().length == 0)
- $(value).gist(viewAjaxCallback);
- });
- //sequence diagram
- const sequences = view.find("div.sequence-diagram.raw").removeClass("raw");
- sequences.each((key, value) => {
- try {
- var $value = $(value);
- const $ele = $(value).parent().parent();
-
- const sequence = $value;
- sequence.sequenceDiagram({
- theme: 'simple'
- });
-
- $ele.addClass('sequence-diagram');
- $value.children().unwrap().unwrap();
- const svg = $ele.find('> svg');
- svg[0].setAttribute('viewBox', `0 0 ${svg.attr('width')} ${svg.attr('height')}`);
- svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet');
- } catch (err) {
- $value.unwrap();
- $value.parent().append('<div class="alert alert-warning">' + err + '</div>');
- console.warn(err);
- }
- });
- //flowchart
- const flow = view.find("div.flow-chart.raw").removeClass("raw");
- flow.each((key, value) => {
- try {
- var $value = $(value);
- const $ele = $(value).parent().parent();
-
- const chart = flowchart.parse($value.text());
- $value.html('');
- chart.drawSVG(value, {
- 'line-width': 2,
- 'fill': 'none',
- 'font-size': '16px',
- 'font-family': "'Andale Mono', monospace"
- });
-
- $ele.addClass('flow-chart');
- $value.children().unwrap().unwrap();
- } catch (err) {
- $value.unwrap();
- $value.parent().append('<div class="alert alert-warning">' + err + '</div>');
- console.warn(err);
- }
- });
- //graphviz
- var graphvizs = view.find("div.graphviz.raw").removeClass("raw");
- graphvizs.each(function (key, value) {
- try {
- var $value = $(value);
- var $ele = $(value).parent().parent();
-
- var graphviz = Viz($value.text());
- if (!graphviz) throw Error('viz.js output empty graph');
- $value.html(graphviz);
-
- $ele.addClass('graphviz');
- $value.children().unwrap().unwrap();
- } catch (err) {
- $value.unwrap();
- $value.parent().append('<div class="alert alert-warning">' + err + '</div>');
- console.warn(err);
- }
- });
- //mermaid
- const mermaids = view.find("div.mermaid.raw").removeClass("raw");
- mermaids.each((key, value) => {
- try {
- var $value = $(value);
- const $ele = $(value).closest('pre');
-
- let mermaidError = null;
- mermaid.parseError = (err, hash) => {
- mermaidError = err;
- };
-
- if (mermaidAPI.parse($value.text())) {
- $ele.addClass('mermaid');
- $ele.html($value.text());
- mermaid.init(undefined, $ele);
- } else {
- throw new Error(mermaidError);
+ $.ajax({
+ type: 'GET',
+ url: `//vimeo.com/api/v2/video/${$(value).attr('data-videoid')}.json`,
+ jsonp: 'callback',
+ dataType: 'jsonp',
+ success (data) {
+ const thumbnailSrc = data[0].thumbnail_large
+ const image = `<img src="${thumbnailSrc}" />`
+ $(value).prepend(image)
+ if (window.viewAjaxCallback) window.viewAjaxCallback()
}
- } catch (err) {
- $value.unwrap();
- $value.parent().append('<div class="alert alert-warning">' + err + '</div>');
- console.warn(err);
- }
- });
- //image href new window(emoji not included)
- const images = view.find("img.raw[src]").removeClass("raw");
- images.each((key, value) => {
+ })
+ })
+ // gist
+ view.find('code[data-gist-id]').each((key, value) => {
+ if ($(value).children().length === 0) { $(value).gist(window.viewAjaxCallback) }
+ })
+ // sequence diagram
+ const sequences = view.find('div.sequence-diagram.raw').removeClass('raw')
+ sequences.each((key, value) => {
+ try {
+ var $value = $(value)
+ const $ele = $(value).parent().parent()
+
+ const sequence = $value
+ sequence.sequenceDiagram({
+ theme: 'simple'
+ })
+
+ $ele.addClass('sequence-diagram')
+ $value.children().unwrap().unwrap()
+ const svg = $ele.find('> svg')
+ svg[0].setAttribute('viewBox', `0 0 ${svg.attr('width')} ${svg.attr('height')}`)
+ svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet')
+ } catch (err) {
+ $value.unwrap()
+ $value.parent().append('<div class="alert alert-warning">' + err + '</div>')
+ console.warn(err)
+ }
+ })
+ // flowchart
+ const flow = view.find('div.flow-chart.raw').removeClass('raw')
+ flow.each((key, value) => {
+ try {
+ var $value = $(value)
+ const $ele = $(value).parent().parent()
+
+ const chart = window.flowchart.parse($value.text())
+ $value.html('')
+ chart.drawSVG(value, {
+ 'line-width': 2,
+ 'fill': 'none',
+ 'font-size': '16px',
+ 'font-family': "'Andale Mono', monospace"
+ })
+
+ $ele.addClass('flow-chart')
+ $value.children().unwrap().unwrap()
+ } catch (err) {
+ $value.unwrap()
+ $value.parent().append('<div class="alert alert-warning">' + err + '</div>')
+ console.warn(err)
+ }
+ })
+ // graphviz
+ var graphvizs = view.find('div.graphviz.raw').removeClass('raw')
+ graphvizs.each(function (key, value) {
+ try {
+ var $value = $(value)
+ var $ele = $(value).parent().parent()
+
+ var graphviz = Viz($value.text())
+ if (!graphviz) throw Error('viz.js output empty graph')
+ $value.html(graphviz)
+
+ $ele.addClass('graphviz')
+ $value.children().unwrap().unwrap()
+ } catch (err) {
+ $value.unwrap()
+ $value.parent().append('<div class="alert alert-warning">' + err + '</div>')
+ console.warn(err)
+ }
+ })
+ // mermaid
+ const mermaids = view.find('div.mermaid.raw').removeClass('raw')
+ mermaids.each((key, value) => {
+ try {
+ var $value = $(value)
+ const $ele = $(value).closest('pre')
+
+ let mermaidError = null
+ window.mermaid.parseError = (err, hash) => {
+ mermaidError = err
+ }
+
+ if (window.mermaidAPI.parse($value.text())) {
+ $ele.addClass('mermaid')
+ $ele.html($value.text())
+ window.mermaid.init(undefined, $ele)
+ } else {
+ throw new Error(mermaidError)
+ }
+ } catch (err) {
+ $value.unwrap()
+ $value.parent().append('<div class="alert alert-warning">' + err + '</div>')
+ console.warn(err)
+ }
+ })
+ // image href new window(emoji not included)
+ const images = view.find('img.raw[src]').removeClass('raw')
+ images.each((key, value) => {
// if it's already wrapped by link, then ignore
- const $value = $(value);
- $value[0].onload = e => {
- if(viewAjaxCallback) viewAjaxCallback();
- };
- });
- //blockquote
- const blockquote = view.find("blockquote.raw").removeClass("raw");
- const blockquote_p = blockquote.find("p");
- blockquote_p.each((key, value) => {
- let html = $(value).html();
- html = replaceExtraTags(html);
- $(value).html(html);
- });
- //color tag in blockquote will change its left border color
- const blockquote_color = blockquote.find(".color");
- blockquote_color.each((key, value) => {
- $(value).closest("blockquote").css('border-left-color', $(value).attr('data-color'));
- });
- //slideshare
- view.find("div.slideshare.raw").removeClass("raw")
+ const $value = $(value)
+ $value[0].onload = e => {
+ if (window.viewAjaxCallback) window.viewAjaxCallback()
+ }
+ })
+ // blockquote
+ const blockquote = view.find('blockquote.raw').removeClass('raw')
+ const blockquoteP = blockquote.find('p')
+ blockquoteP.each((key, value) => {
+ let html = $(value).html()
+ html = replaceExtraTags(html)
+ $(value).html(html)
+ })
+ // color tag in blockquote will change its left border color
+ const blockquoteColor = blockquote.find('.color')
+ blockquoteColor.each((key, value) => {
+ $(value).closest('blockquote').css('border-left-color', $(value).attr('data-color'))
+ })
+ // slideshare
+ view.find('div.slideshare.raw').removeClass('raw')
.each((key, value) => {
- $.ajax({
- type: 'GET',
- url: `//www.slideshare.net/api/oembed/2?url=http://www.slideshare.net/${$(value).attr('data-slideshareid')}&format=json`,
- jsonp: 'callback',
- dataType: 'jsonp',
- success(data) {
- const $html = $(data.html);
- const iframe = $html.closest('iframe');
- const caption = $html.closest('div');
- const inner = $('<div class="inner"></div>').append(iframe);
- const height = iframe.attr('height');
- const width = iframe.attr('width');
- const ratio = (height / width) * 100;
- inner.css('padding-bottom', `${ratio}%`);
- $(value).html(inner).append(caption);
- if(viewAjaxCallback) viewAjaxCallback();
- }
- });
- });
- //speakerdeck
- view.find("div.speakerdeck.raw").removeClass("raw")
+ $.ajax({
+ type: 'GET',
+ url: `//www.slideshare.net/api/oembed/2?url=http://www.slideshare.net/${$(value).attr('data-slideshareid')}&format=json`,
+ jsonp: 'callback',
+ dataType: 'jsonp',
+ success (data) {
+ const $html = $(data.html)
+ const iframe = $html.closest('iframe')
+ const caption = $html.closest('div')
+ const inner = $('<div class="inner"></div>').append(iframe)
+ const height = iframe.attr('height')
+ const width = iframe.attr('width')
+ const ratio = (height / width) * 100
+ inner.css('padding-bottom', `${ratio}%`)
+ $(value).html(inner).append(caption)
+ if (window.viewAjaxCallback) window.viewAjaxCallback()
+ }
+ })
+ })
+ // speakerdeck
+ view.find('div.speakerdeck.raw').removeClass('raw')
.each((key, value) => {
- const url = `https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F${encodeURIComponent($(value).attr('data-speakerdeckid'))}`;
- //use yql because speakerdeck not support jsonp
- $.ajax({
- url: 'https://query.yahooapis.com/v1/public/yql',
- data: {
- q: `select * from json where url ='${url}'`,
- format: "json"
- },
- dataType: "jsonp",
- success(data) {
- if (!data.query || !data.query.results) return;
- const json = data.query.results.json;
- const html = json.html;
- var ratio = json.height / json.width;
- $(value).html(html);
- const iframe = $(value).children('iframe');
- const src = iframe.attr('src');
- if (src.indexOf('//') == 0)
- iframe.attr('src', `https:${src}`);
- const inner = $('<div class="inner"></div>').append(iframe);
- const height = iframe.attr('height');
- const width = iframe.attr('width');
- var ratio = (height / width) * 100;
- inner.css('padding-bottom', `${ratio}%`);
- $(value).html(inner);
- if(viewAjaxCallback) viewAjaxCallback();
- }
- });
- });
- //pdf
- view.find("div.pdf.raw").removeClass("raw")
+ const url = `https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F${encodeURIComponent($(value).attr('data-speakerdeckid'))}`
+ // use yql because speakerdeck not support jsonp
+ $.ajax({
+ url: 'https://query.yahooapis.com/v1/public/yql',
+ data: {
+ q: `select * from json where url ='${url}'`,
+ format: 'json'
+ },
+ dataType: 'jsonp',
+ success (data) {
+ if (!data.query || !data.query.results) return
+ const json = data.query.results.json
+ const html = json.html
+ var ratio = json.height / json.width
+ $(value).html(html)
+ const iframe = $(value).children('iframe')
+ const src = iframe.attr('src')
+ if (src.indexOf('//') === 0) { iframe.attr('src', `https:${src}`) }
+ const inner = $('<div class="inner"></div>').append(iframe)
+ const height = iframe.attr('height')
+ const width = iframe.attr('width')
+ ratio = (height / width) * 100
+ inner.css('padding-bottom', `${ratio}%`)
+ $(value).html(inner)
+ if (window.viewAjaxCallback) window.viewAjaxCallback()
+ }
+ })
+ })
+ // pdf
+ view.find('div.pdf.raw').removeClass('raw')
.each(function (key, value) {
- const url = $(value).attr('data-pdfurl');
- const inner = $('<div></div>');
- $(this).append(inner);
- PDFObject.embed(url, inner, {
- height: '400px'
- });
- });
- //syntax highlighting
- view.find("code.raw").removeClass("raw")
+ const url = $(value).attr('data-pdfurl')
+ const inner = $('<div></div>')
+ $(this).append(inner)
+ PDFObject.embed(url, inner, {
+ height: '400px'
+ })
+ })
+ // syntax highlighting
+ view.find('code.raw').removeClass('raw')
.each((key, value) => {
- const langDiv = $(value);
- if (langDiv.length > 0) {
- const reallang = langDiv[0].className.replace(/hljs|wrap/g, '').trim();
- const codeDiv = langDiv.find('.code');
- let code = "";
- if (codeDiv.length > 0) code = codeDiv.html();
- else code = langDiv.html();
- if (!reallang) {
- var result = {
- value: code
- };
- } else if (reallang == "haskell" || reallang == "go" || reallang == "typescript" || reallang == "jsx") {
- code = S(code).unescapeHTML().s;
- var result = {
- value: Prism.highlight(code, Prism.languages[reallang])
- };
- } else if (reallang == "tiddlywiki" || reallang == "mediawiki") {
- code = S(code).unescapeHTML().s;
- var result = {
- value: Prism.highlight(code, Prism.languages.wiki)
- };
- } else {
- code = S(code).unescapeHTML().s;
- const languages = hljs.listLanguages();
- if (!languages.includes(reallang)) {
- var result = hljs.highlightAuto(code);
- } else {
- var result = hljs.highlight(reallang, code);
- }
- }
- if (codeDiv.length > 0) codeDiv.html(result.value);
- else langDiv.html(result.value);
+ const langDiv = $(value)
+ if (langDiv.length > 0) {
+ const reallang = langDiv[0].className.replace(/hljs|wrap/g, '').trim()
+ const codeDiv = langDiv.find('.code')
+ let code = ''
+ if (codeDiv.length > 0) code = codeDiv.html()
+ else code = langDiv.html()
+ var result
+ if (!reallang) {
+ result = {
+ value: code
+ }
+ } else if (reallang === 'haskell' || reallang === 'go' || reallang === 'typescript' || reallang === 'jsx') {
+ code = S(code).unescapeHTML().s
+ result = {
+ value: Prism.highlight(code, Prism.languages[reallang])
+ }
+ } else if (reallang === 'tiddlywiki' || reallang === 'mediawiki') {
+ code = S(code).unescapeHTML().s
+ result = {
+ value: Prism.highlight(code, Prism.languages.wiki)
+ }
+ } else {
+ code = S(code).unescapeHTML().s
+ const languages = hljs.listLanguages()
+ if (!languages.includes(reallang)) {
+ result = hljs.highlightAuto(code)
+ } else {
+ result = hljs.highlight(reallang, code)
+ }
}
- });
- //mathjax
- const mathjaxdivs = view.find('span.mathjax.raw').removeClass("raw").toArray();
- try {
- if (mathjaxdivs.length > 1) {
- MathJax.Hub.Queue(["Typeset", MathJax.Hub, mathjaxdivs]);
- MathJax.Hub.Queue(viewAjaxCallback);
- } else if (mathjaxdivs.length > 0) {
- MathJax.Hub.Queue(["Typeset", MathJax.Hub, mathjaxdivs[0]]);
- MathJax.Hub.Queue(viewAjaxCallback);
- }
- } catch (err) {
- console.warn(err);
+ if (codeDiv.length > 0) codeDiv.html(result.value)
+ else langDiv.html(result.value)
+ }
+ })
+ // mathjax
+ const mathjaxdivs = view.find('span.mathjax.raw').removeClass('raw').toArray()
+ try {
+ if (mathjaxdivs.length > 1) {
+ window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, mathjaxdivs])
+ window.MathJax.Hub.Queue(window.viewAjaxCallback)
+ } else if (mathjaxdivs.length > 0) {
+ window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, mathjaxdivs[0]])
+ window.MathJax.Hub.Queue(window.viewAjaxCallback)
}
- //render title
- document.title = renderTitle(view);
+ } catch (err) {
+ console.warn(err)
+ }
+ // render title
+ document.title = renderTitle(view)
}
-//only static transform should be here
-export function postProcess(code) {
- const result = $(`<div>${code}</div>`);
- //link should open in new window or tab
- result.find('a:not([href^="#"]):not([target])').attr('target', '_blank');
- //update continue line numbers
- const linenumberdivs = result.find('.gutter.linenumber').toArray();
- for (let i = 0; i < linenumberdivs.length; i++) {
- if ($(linenumberdivs[i]).hasClass('continue')) {
- const startnumber = linenumberdivs[i - 1] ? parseInt($(linenumberdivs[i - 1]).find('> span').last().attr('data-linenumber')) : 0;
- $(linenumberdivs[i]).find('> span').each((key, value) => {
- $(value).attr('data-linenumber', startnumber + key + 1);
- });
- }
- }
- // show yaml meta paring error
- if (md.metaError) {
- var warning = result.find('div#meta-error');
- if (warning && warning.length > 0) {
- warning.text(md.metaError)
- } else {
- warning = $('<div id="meta-error" class="alert alert-warning">' + md.metaError + '</div>')
- result.prepend(warning);
- }
+// only static transform should be here
+export function postProcess (code) {
+ const result = $(`<div>${code}</div>`)
+ // link should open in new window or tab
+ result.find('a:not([href^="#"]):not([target])').attr('target', '_blank')
+ // update continue line numbers
+ const linenumberdivs = result.find('.gutter.linenumber').toArray()
+ for (let i = 0; i < linenumberdivs.length; i++) {
+ if ($(linenumberdivs[i]).hasClass('continue')) {
+ const startnumber = linenumberdivs[i - 1] ? parseInt($(linenumberdivs[i - 1]).find('> span').last().attr('data-linenumber')) : 0
+ $(linenumberdivs[i]).find('> span').each((key, value) => {
+ $(value).attr('data-linenumber', startnumber + key + 1)
+ })
+ }
+ }
+ // show yaml meta paring error
+ if (md.metaError) {
+ var warning = result.find('div#meta-error')
+ if (warning && warning.length > 0) {
+ warning.text(md.metaError)
+ } else {
+ warning = $('<div id="meta-error" class="alert alert-warning">' + md.metaError + '</div>')
+ result.prepend(warning)
}
- return result;
+ }
+ return result
}
-window.postProcess = postProcess;
-
-function generateCleanHTML(view) {
- const src = view.clone();
- const eles = src.find('*');
- //remove syncscroll parts
- eles.removeClass('part');
- src.find('*[class=""]').removeAttr('class');
- eles.removeAttr('data-startline data-endline');
- src.find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll');
- //remove gist content
- src.find("code[data-gist-id]").children().remove();
- //disable todo list
- src.find("input.task-list-item-checkbox").attr('disabled', '');
- //replace emoji image path
- src.find("img.emoji").each((key, value) => {
- let name = $(value).attr('alt');
- name = name.substr(1);
- name = name.slice(0, name.length - 1);
- $(value).attr('src', `https://www.tortue.me/emoji/${name}.png`);
- });
- //replace video to iframe
- src.find("div[data-videoid]").each((key, value) => {
- const id = $(value).attr('data-videoid');
- const style = $(value).attr('style');
- let url = null;
- if ($(value).hasClass('youtube')) {
- url = 'https://www.youtube.com/embed/';
- } else if ($(value).hasClass('vimeo')) {
- url = 'https://player.vimeo.com/video/';
- }
- if (url) {
- const iframe = $('<iframe frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>');
- iframe.attr('src', url + id);
- iframe.attr('style', style);
- $(value).html(iframe);
- }
- });
- return src;
+window.postProcess = postProcess
+
+function generateCleanHTML (view) {
+ const src = view.clone()
+ const eles = src.find('*')
+ // remove syncscroll parts
+ eles.removeClass('part')
+ src.find('*[class=""]').removeAttr('class')
+ eles.removeAttr('data-startline data-endline')
+ src.find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll')
+ // remove gist content
+ src.find('code[data-gist-id]').children().remove()
+ // disable todo list
+ src.find('input.task-list-item-checkbox').attr('disabled', '')
+ // replace emoji image path
+ src.find('img.emoji').each((key, value) => {
+ let name = $(value).attr('alt')
+ name = name.substr(1)
+ name = name.slice(0, name.length - 1)
+ $(value).attr('src', `https://www.tortue.me/emoji/${name}.png`)
+ })
+ // replace video to iframe
+ src.find('div[data-videoid]').each((key, value) => {
+ const id = $(value).attr('data-videoid')
+ const style = $(value).attr('style')
+ let url = null
+ if ($(value).hasClass('youtube')) {
+ url = 'https://www.youtube.com/embed/'
+ } else if ($(value).hasClass('vimeo')) {
+ url = 'https://player.vimeo.com/video/'
+ }
+ if (url) {
+ const iframe = $('<iframe frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>')
+ iframe.attr('src', url + id)
+ iframe.attr('style', style)
+ $(value).html(iframe)
+ }
+ })
+ return src
}
-export function exportToRawHTML(view) {
- const filename = `${renderFilename(ui.area.markdown)}.html`;
- const src = generateCleanHTML(view);
- $(src).find('a.anchor').remove();
- const html = src[0].outerHTML;
- const blob = new Blob([html], {
- type: "text/html;charset=utf-8"
- });
- saveAs(blob, filename, true);
+export function exportToRawHTML (view) {
+ const filename = `${renderFilename(window.ui.area.markdown)}.html`
+ const src = generateCleanHTML(view)
+ $(src).find('a.anchor').remove()
+ const html = src[0].outerHTML
+ const blob = new Blob([html], {
+ type: 'text/html;charset=utf-8'
+ })
+ saveAs(blob, filename, true)
}
-//extract markdown body to html and compile to template
-export function exportToHTML(view) {
- const title = renderTitle(ui.area.markdown);
- const filename = `${renderFilename(ui.area.markdown)}.html`;
- const src = generateCleanHTML(view);
- //generate toc
- const toc = $('#ui-toc').clone();
- toc.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll');
- const tocAffix = $('#ui-toc-affix').clone();
- tocAffix.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll');
- //generate html via template
- $.get(`${serverurl}/build/html.min.css`, css => {
- $.get(`${serverurl}/views/html.hbs`, data => {
- const template = Handlebars.compile(data);
- const context = {
- url: serverurl,
- title,
- css,
- html: src[0].outerHTML,
- 'ui-toc': toc.html(),
- 'ui-toc-affix': tocAffix.html(),
- lang: (md && md.meta && md.meta.lang) ? `lang="${md.meta.lang}"` : null,
- dir: (md && md.meta && md.meta.dir) ? `dir="${md.meta.dir}"` : null
- };
- const html = template(context);
+// extract markdown body to html and compile to template
+export function exportToHTML (view) {
+ const title = renderTitle(window.ui.area.markdown)
+ const filename = `${renderFilename(window.ui.area.markdown)}.html`
+ const src = generateCleanHTML(view)
+ // generate toc
+ const toc = $('#ui-toc').clone()
+ toc.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll')
+ const tocAffix = $('#ui-toc-affix').clone()
+ tocAffix.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll')
+ // generate html via template
+ $.get(`${serverurl}/build/html.min.css`, css => {
+ $.get(`${serverurl}/views/html.hbs`, data => {
+ const template = window.Handlebars.compile(data)
+ const context = {
+ url: serverurl,
+ title,
+ css,
+ html: src[0].outerHTML,
+ 'ui-toc': toc.html(),
+ 'ui-toc-affix': tocAffix.html(),
+ lang: (md && md.meta && md.meta.lang) ? `lang="${md.meta.lang}"` : null,
+ dir: (md && md.meta && md.meta.dir) ? `dir="${md.meta.dir}"` : null
+ }
+ const html = template(context)
// console.log(html);
- const blob = new Blob([html], {
- type: "text/html;charset=utf-8"
- });
- saveAs(blob, filename, true);
- });
- });
+ const blob = new Blob([html], {
+ type: 'text/html;charset=utf-8'
+ })
+ saveAs(blob, filename, true)
+ })
+ })
}
-//jQuery sortByDepth
+// jQuery sortByDepth
$.fn.sortByDepth = function () {
- const ar = this.map(function () {
- return {
- length: $(this).parents().length,
- elt: this
- }
- }).get();
-
- const result = [];
- let i = ar.length;
- ar.sort((a, b) => a.length - b.length);
- while (i--) {
- result.push(ar[i].elt);
- }
- return $(result);
-};
-
-function toggleTodoEvent(e) {
- const startline = $(this).closest('li').attr('data-startline') - 1;
- const line = editor.getLine(startline);
- const matches = line.match(/^[>\s]*[\-\+\*]\s\[([x ])\]/);
- if (matches && matches.length >= 2) {
- let checked = null;
- if (matches[1] == 'x')
- checked = true;
- else if (matches[1] == ' ')
- checked = false;
- const replacements = matches[0].match(/(^[>\s]*[\-\+\*]\s\[)([x ])(\])/);
- editor.replaceRange(checked ? ' ' : 'x', {
- line: startline,
- ch: replacements[1].length
- }, {
- line: startline,
- ch: replacements[1].length + 1
- }, '+input');
+ const ar = this.map(function () {
+ return {
+ length: $(this).parents().length,
+ elt: this
}
+ }).get()
+
+ const result = []
+ let i = ar.length
+ ar.sort((a, b) => a.length - b.length)
+ while (i--) {
+ result.push(ar[i].elt)
+ }
+ return $(result)
}
-//remove hash
-function removeHash() {
- history.pushState("", document.title, window.location.pathname + window.location.search);
+function toggleTodoEvent (e) {
+ const startline = $(this).closest('li').attr('data-startline') - 1
+ const line = window.editor.getLine(startline)
+ const matches = line.match(/^[>\s]*[-+*]\s\[([x ])\]/)
+ if (matches && matches.length >= 2) {
+ let checked = null
+ if (matches[1] === 'x') { checked = true } else if (matches[1] === ' ') { checked = false }
+ const replacements = matches[0].match(/(^[>\s]*[-+*]\s\[)([x ])(\])/)
+ window.editor.replaceRange(checked ? ' ' : 'x', {
+ line: startline,
+ ch: replacements[1].length
+ }, {
+ line: startline,
+ ch: replacements[1].length + 1
+ }, '+input')
+ }
}
-let tocExpand = false;
+// remove hash
+function removeHash () {
+ history.pushState('', document.title, window.location.pathname + window.location.search)
+}
-function checkExpandToggle() {
- const toc = $('.ui-toc-dropdown .toc');
- const toggle = $('.expand-toggle');
- if (!tocExpand) {
- toc.removeClass('expand');
- toggle.text('Expand all');
- } else {
- toc.addClass('expand');
- toggle.text('Collapse all');
- }
+let tocExpand = false
+
+function checkExpandToggle () {
+ const toc = $('.ui-toc-dropdown .toc')
+ const toggle = $('.expand-toggle')
+ if (!tocExpand) {
+ toc.removeClass('expand')
+ toggle.text('Expand all')
+ } else {
+ toc.addClass('expand')
+ toggle.text('Collapse all')
+ }
}
-//toc
-export function generateToc(id) {
- const target = $(`#${id}`);
- target.html('');
- new Toc('doc', {
- 'level': 3,
- 'top': -1,
- 'class': 'toc',
- 'ulClass': 'nav',
- 'targetId': id,
- 'process': getHeaderContent
- });
- if (target.text() == 'undefined')
- target.html('');
- const tocMenu = $('<div class="toc-menu"></div');
- const toggle = $('<a class="expand-toggle" href="#">Expand all</a>');
- const backtotop = $('<a class="back-to-top" href="#">Back to top</a>');
- const gotobottom = $('<a class="go-to-bottom" href="#">Go to bottom</a>');
- checkExpandToggle();
- toggle.click(e => {
- e.preventDefault();
- e.stopPropagation();
- tocExpand = !tocExpand;
- checkExpandToggle();
- });
- backtotop.click(e => {
- e.preventDefault();
- e.stopPropagation();
- if (scrollToTop)
- scrollToTop();
- removeHash();
- });
- gotobottom.click(e => {
- e.preventDefault();
- e.stopPropagation();
- if (scrollToBottom)
- scrollToBottom();
- removeHash();
- });
- tocMenu.append(toggle).append(backtotop).append(gotobottom);
- target.append(tocMenu);
+// toc
+export function generateToc (id) {
+ const target = $(`#${id}`)
+ target.html('')
+ /* eslint-disable no-unused-vars */
+ var toc = new window.Toc('doc', {
+ 'level': 3,
+ 'top': -1,
+ 'class': 'toc',
+ 'ulClass': 'nav',
+ 'targetId': id,
+ 'process': getHeaderContent
+ })
+ /* eslint-enable no-unsed-vars */
+ if (target.text() === 'undefined') { target.html('') }
+ const tocMenu = $('<div class="toc-menu"></div')
+ const toggle = $('<a class="expand-toggle" href="#">Expand all</a>')
+ const backtotop = $('<a class="back-to-top" href="#">Back to top</a>')
+ const gotobottom = $('<a class="go-to-bottom" href="#">Go to bottom</a>')
+ checkExpandToggle()
+ toggle.click(e => {
+ e.preventDefault()
+ e.stopPropagation()
+ tocExpand = !tocExpand
+ checkExpandToggle()
+ })
+ backtotop.click(e => {
+ e.preventDefault()
+ e.stopPropagation()
+ if (window.scrollToTop) { window.scrollToTop() }
+ removeHash()
+ })
+ gotobottom.click(e => {
+ e.preventDefault()
+ e.stopPropagation()
+ if (window.scrollToBottom) { window.scrollToBottom() }
+ removeHash()
+ })
+ tocMenu.append(toggle).append(backtotop).append(gotobottom)
+ target.append(tocMenu)
}
-//smooth all hash trigger scrolling
-export function smoothHashScroll() {
- const hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray();
+// smooth all hash trigger scrolling
+export function smoothHashScroll () {
+ const hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray()
- for (const element of hashElements) {
- const $element = $(element);
- const hash = element.hash;
- if (hash) {
- $element.on('click', function (e) {
+ for (const element of hashElements) {
+ const $element = $(element)
+ const hash = element.hash
+ if (hash) {
+ $element.on('click', function (e) {
// store hash
- const hash = decodeURIComponent(this.hash);
+ const hash = decodeURIComponent(this.hash)
// escape special characters in jquery selector
- const $hash = $(hash.replace(/(:|\.|\[|\]|,)/g, "\\$1"));
+ const $hash = $(hash.replace(/(:|\.|\[|\]|,)/g, '\\$1'))
// return if no element been selected
- if ($hash.length <= 0) return;
+ if ($hash.length <= 0) return
// prevent default anchor click behavior
- e.preventDefault();
+ e.preventDefault()
// animate
- $('body, html').stop(true, true).animate({
- scrollTop: $hash.offset().top
- }, 100, "linear", () => {
+ $('body, html').stop(true, true).animate({
+ scrollTop: $hash.offset().top
+ }, 100, 'linear', () => {
// when done, add hash to url
// (default click behaviour)
- window.location.hash = hash;
- });
- });
- $element.attr('smoothhashscroll', '');
- }
+ window.location.hash = hash
+ })
+ })
+ $element.attr('smoothhashscroll', '')
}
+ }
}
-function imgPlayiframe(element, src) {
- if (!$(element).attr("data-videoid")) return;
- const iframe = $("<iframe frameborder='0' webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>");
- $(iframe).attr("src", `${src + $(element).attr("data-videoid")}?autoplay=1`);
- $(element).find('img').css('visibility', 'hidden');
- $(element).append(iframe);
+function imgPlayiframe (element, src) {
+ if (!$(element).attr('data-videoid')) return
+ const iframe = $("<iframe frameborder='0' webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>")
+ $(iframe).attr('src', `${src + $(element).attr('data-videoid')}?autoplay=1`)
+ $(element).find('img').css('visibility', 'hidden')
+ $(element).append(iframe)
}
const anchorForId = id => {
- const anchor = document.createElement("a");
- anchor.className = "anchor hidden-xs";
- anchor.href = `#${id}`;
- anchor.innerHTML = "<span class=\"octicon octicon-link\"></span>";
- anchor.title = id;
- return anchor;
-};
+ const anchor = document.createElement('a')
+ anchor.className = 'anchor hidden-xs'
+ anchor.href = `#${id}`
+ anchor.innerHTML = '<span class="octicon octicon-link"></span>'
+ anchor.title = id
+ return anchor
+}
const linkifyAnchors = (level, containingElement) => {
- const headers = containingElement.getElementsByTagName(`h${level}`);
-
- for (let i = 0, l = headers.length; i < l; i++) {
- let header = headers[i];
- if (header.getElementsByClassName("anchor").length == 0) {
- if (typeof header.id == "undefined" || header.id == "") {
- //to escape characters not allow in css and humanize
- const id = slugifyWithUTF8(getHeaderContent(header));
- header.id = id;
- }
- header.insertBefore(anchorForId(header.id), header.firstChild);
- }
+ const headers = containingElement.getElementsByTagName(`h${level}`)
+
+ for (let i = 0, l = headers.length; i < l; i++) {
+ let header = headers[i]
+ if (header.getElementsByClassName('anchor').length === 0) {
+ if (typeof header.id === 'undefined' || header.id === '') {
+ // to escape characters not allow in css and humanize
+ const id = slugifyWithUTF8(getHeaderContent(header))
+ header.id = id
+ }
+ header.insertBefore(anchorForId(header.id), header.firstChild)
}
-};
+ }
+}
-export function autoLinkify(view) {
- const contentBlock = view[0];
- if (!contentBlock) {
- return;
- }
- for (let level = 1; level <= 6; level++) {
- linkifyAnchors(level, contentBlock);
- }
+export function autoLinkify (view) {
+ const contentBlock = view[0]
+ if (!contentBlock) {
+ return
+ }
+ for (let level = 1; level <= 6; level++) {
+ linkifyAnchors(level, contentBlock)
+ }
}
-function getHeaderContent(header) {
- const headerHTML = $(header).clone();
- headerHTML.find('.MathJax_Preview').remove();
- headerHTML.find('.MathJax').remove();
- return headerHTML[0].innerHTML;
+function getHeaderContent (header) {
+ const headerHTML = $(header).clone()
+ headerHTML.find('.MathJax_Preview').remove()
+ headerHTML.find('.MathJax').remove()
+ return headerHTML[0].innerHTML
}
-export function deduplicatedHeaderId(view) {
- const headers = view.find(':header.raw').removeClass('raw').toArray();
- for (let i = 0; i < headers.length; i++) {
- const id = $(headers[i]).attr('id');
- if (!id) continue;
- const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray();
- for (let j = 0; j < duplicatedHeaders.length; j++) {
- if (duplicatedHeaders[j] != headers[i]) {
- const newId = id + j;
- const $duplicatedHeader = $(duplicatedHeaders[j]);
- $duplicatedHeader.attr('id', newId);
- const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`);
- $headerLink.attr('href', `#${newId}`);
- $headerLink.attr('title', newId);
- }
- }
+export function deduplicatedHeaderId (view) {
+ const headers = view.find(':header.raw').removeClass('raw').toArray()
+ for (let i = 0; i < headers.length; i++) {
+ const id = $(headers[i]).attr('id')
+ if (!id) continue
+ const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray()
+ for (let j = 0; j < duplicatedHeaders.length; j++) {
+ if (duplicatedHeaders[j] !== headers[i]) {
+ const newId = id + j
+ const $duplicatedHeader = $(duplicatedHeaders[j])
+ $duplicatedHeader.attr('id', newId)
+ const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`)
+ $headerLink.attr('href', `#${newId}`)
+ $headerLink.attr('title', newId)
+ }
}
+ }
}
-export function renderTOC(view) {
- const tocs = view.find('.toc').toArray();
- for (let i = 0; i < tocs.length; i++) {
- const toc = $(tocs[i]);
- const id = `toc${i}`;
- toc.attr('id', id);
- const target = $(`#${id}`);
- target.html('');
- new Toc('doc', {
- 'level': 3,
- 'top': -1,
- 'class': 'toc',
- 'targetId': id,
- 'process': getHeaderContent
- });
- if (target.text() == 'undefined')
- target.html('');
- target.replaceWith(target.html());
- }
+export function renderTOC (view) {
+ const tocs = view.find('.toc').toArray()
+ for (let i = 0; i < tocs.length; i++) {
+ const toc = $(tocs[i])
+ const id = `toc${i}`
+ toc.attr('id', id)
+ const target = $(`#${id}`)
+ target.html('')
+ /* eslint-disable no-unused-vars */
+ var toc = new window.Toc('doc', {
+ 'level': 3,
+ 'top': -1,
+ 'class': 'toc',
+ 'targetId': id,
+ 'process': getHeaderContent
+ })
+ /* eslint-enable no-unused-vars */
+ if (target.text() === 'undefined') { target.html('') }
+ target.replaceWith(target.html())
+ }
}
-export function scrollToHash() {
- const hash = location.hash;
- location.hash = "";
- location.hash = hash;
+export function scrollToHash () {
+ const hash = location.hash
+ location.hash = ''
+ location.hash = hash
}
-function highlightRender(code, lang) {
- if (!lang || /no(-?)highlight|plain|text/.test(lang))
- return;
- code = S(code).escapeHTML().s
- if (lang == 'sequence') {
- return `<div class="sequence-diagram raw">${code}</div>`;
- } else if (lang == 'flow') {
- return `<div class="flow-chart raw">${code}</div>`;
- } else if (lang == 'graphviz') {
- return `<div class="graphviz raw">${code}</div>`;
- } else if (lang == 'mermaid') {
- return `<div class="mermaid raw">${code}</div>`;
+function highlightRender (code, lang) {
+ if (!lang || /no(-?)highlight|plain|text/.test(lang)) { return }
+ code = S(code).escapeHTML().s
+ if (lang === 'sequence') {
+ return `<div class="sequence-diagram raw">${code}</div>`
+ } else if (lang === 'flow') {
+ return `<div class="flow-chart raw">${code}</div>`
+ } else if (lang === 'graphviz') {
+ return `<div class="graphviz raw">${code}</div>`
+ } else if (lang === 'mermaid') {
+ return `<div class="mermaid raw">${code}</div>`
+ }
+ const result = {
+ value: code
+ }
+ const showlinenumbers = /=$|=\d+$|=\+$/.test(lang)
+ if (showlinenumbers) {
+ let startnumber = 1
+ const matches = lang.match(/=(\d+)$/)
+ if (matches) { startnumber = parseInt(matches[1]) }
+ const lines = result.value.split('\n')
+ const linenumbers = []
+ for (let i = 0; i < lines.length - 1; i++) {
+ linenumbers[i] = `<span data-linenumber='${startnumber + i}'></span>`
}
- const result = {
- value: code
- };
- const showlinenumbers = /\=$|\=\d+$|\=\+$/.test(lang);
- if (showlinenumbers) {
- let startnumber = 1;
- const matches = lang.match(/\=(\d+)$/);
- if (matches)
- startnumber = parseInt(matches[1]);
- const lines = result.value.split('\n');
- const linenumbers = [];
- for (let i = 0; i < lines.length - 1; i++) {
- linenumbers[i] = `<span data-linenumber='${startnumber + i}'></span>`;
- }
- const continuelinenumber = /\=\+$/.test(lang);
- const linegutter = `<div class='gutter linenumber${continuelinenumber ? " continue" : ""}'>${linenumbers.join('\n')}</div>`;
- result.value = `<div class='wrapper'>${linegutter}<div class='code'>${result.value}</div></div>`;
- }
- return result.value;
+ const continuelinenumber = /=\+$/.test(lang)
+ const linegutter = `<div class='gutter linenumber${continuelinenumber ? ' continue' : ''}'>${linenumbers.join('\n')}</div>`
+ result.value = `<div class='wrapper'>${linegutter}<div class='code'>${result.value}</div></div>`
+ }
+ return result.value
}
-import markdownit from 'markdown-it';
-import markdownitContainer from 'markdown-it-container';
+import markdownit from 'markdown-it'
+import markdownitContainer from 'markdown-it-container'
export let md = markdownit('default', {
- html: true,
- breaks: true,
- langPrefix: "",
- linkify: true,
- typographer: true,
- highlight: highlightRender
-});
-window.md = md;
-
-md.use(require('markdown-it-abbr'));
-md.use(require('markdown-it-footnote'));
-md.use(require('markdown-it-deflist'));
-md.use(require('markdown-it-mark'));
-md.use(require('markdown-it-ins'));
-md.use(require('markdown-it-sub'));
-md.use(require('markdown-it-sup'));
+ html: true,
+ breaks: true,
+ langPrefix: '',
+ linkify: true,
+ typographer: true,
+ highlight: highlightRender
+})
+window.md = md
+
+md.use(require('markdown-it-abbr'))
+md.use(require('markdown-it-footnote'))
+md.use(require('markdown-it-deflist'))
+md.use(require('markdown-it-mark'))
+md.use(require('markdown-it-ins'))
+md.use(require('markdown-it-sub'))
+md.use(require('markdown-it-sup'))
md.use(require('markdown-it-mathjax')({
- beforeMath: '<span class="mathjax raw">',
- afterMath: '</span>',
- beforeInlineMath: '<span class="mathjax raw">\\(',
- afterInlineMath: '\\)</span>',
- beforeDisplayMath: '<span class="mathjax raw">\\[',
- afterDisplayMath: '\\]</span>'
-}));
-md.use(require('markdown-it-imsize'));
+ beforeMath: '<span class="mathjax raw">',
+ afterMath: '</span>',
+ beforeInlineMath: '<span class="mathjax raw">\\(',
+ afterInlineMath: '\\)</span>',
+ beforeDisplayMath: '<span class="mathjax raw">\\[',
+ afterDisplayMath: '\\]</span>'
+}))
+md.use(require('markdown-it-imsize'))
md.use(require('markdown-it-emoji'), {
- shortcuts: {}
-});
-
-emojify.setConfig({
- blacklist: {
- elements: ['script', 'textarea', 'a', 'pre', 'code', 'svg'],
- classes: ['no-emojify']
- },
- img_dir: `${serverurl}/build/emojify.js/dist/images/basic`,
- ignore_emoticons: true
-});
-
-md.renderer.rules.emoji = (token, idx) => emojify.replace(`:${token[idx].markup}:`);
-
-function renderContainer(tokens, idx, options, env, self) {
- tokens[idx].attrJoin('role', 'alert');
- tokens[idx].attrJoin('class', 'alert');
- tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`);
- return self.renderToken(...arguments);
+ shortcuts: {}
+})
+
+window.emojify.setConfig({
+ blacklist: {
+ elements: ['script', 'textarea', 'a', 'pre', 'code', 'svg'],
+ classes: ['no-emojify']
+ },
+ img_dir: `${serverurl}/build/emojify.js/dist/images/basic`,
+ ignore_emoticons: true
+})
+
+md.renderer.rules.emoji = (token, idx) => window.emojify.replace(`:${token[idx].markup}:`)
+
+function renderContainer (tokens, idx, options, env, self) {
+ tokens[idx].attrJoin('role', 'alert')
+ tokens[idx].attrJoin('class', 'alert')
+ tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`)
+ return self.renderToken(...arguments)
}
-md.use(markdownitContainer, 'success', { render: renderContainer });
-md.use(markdownitContainer, 'info', { render: renderContainer });
-md.use(markdownitContainer, 'warning', { render: renderContainer });
-md.use(markdownitContainer, 'danger', { render: renderContainer });
+md.use(markdownitContainer, 'success', { render: renderContainer })
+md.use(markdownitContainer, 'info', { render: renderContainer })
+md.use(markdownitContainer, 'warning', { render: renderContainer })
+md.use(markdownitContainer, 'danger', { render: renderContainer })
md.renderer.rules.image = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ return self.renderToken(...arguments)
+}
md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ return self.renderToken(...arguments)
+}
md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ return self.renderToken(...arguments)
+}
md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ return self.renderToken(...arguments)
+}
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
- const token = tokens[idx];
- const info = token.info ? md.utils.unescapeAll(token.info).trim() : '';
- let langName = '';
- let highlighted;
-
- if (info) {
- langName = info.split(/\s+/g)[0];
- if (/\!$/.test(info)) token.attrJoin('class', 'wrap');
- token.attrJoin('class', options.langPrefix + langName.replace(/\=$|\=\d+$|\=\+$|\!$|\=\!$/, ''));
- token.attrJoin('class', 'hljs');
- token.attrJoin('class', 'raw');
- }
-
- if (options.highlight) {
- highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content);
- } else {
- highlighted = md.utils.escapeHtml(token.content);
- }
-
- if (highlighted.indexOf('<pre') === 0) {
- return `${highlighted}\n`;
- }
-
- return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`;
-};
+ const token = tokens[idx]
+ const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
+ let langName = ''
+ let highlighted
+
+ if (info) {
+ langName = info.split(/\s+/g)[0]
+ if (/!$/.test(info)) token.attrJoin('class', 'wrap')
+ token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!$/, ''))
+ token.attrJoin('class', 'hljs')
+ token.attrJoin('class', 'raw')
+ }
+
+ if (options.highlight) {
+ highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content)
+ } else {
+ highlighted = md.utils.escapeHtml(token.content)
+ }
+
+ if (highlighted.indexOf('<pre') === 0) {
+ return `${highlighted}\n`
+ }
+
+ return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`
+}
/* Defined regex markdown it plugins */
-import Plugin from 'markdown-it-regexp';
+import Plugin from 'markdown-it-regexp'
-//youtube
+// youtube
const youtubePlugin = new Plugin(
// regexp to match
/{%youtube\s*([\d\D]*?)\s*%}/,
(match, utils) => {
- const videoid = match[1];
- if (!videoid) return;
- const div = $('<div class="youtube raw"></div>');
- div.attr('data-videoid', videoid);
- const thumbnail_src = `//img.youtube.com/vi/${videoid}/hqdefault.jpg`;
- const image = `<img src="${thumbnail_src}" />`;
- div.append(image);
- const icon = '<i class="icon fa fa-youtube-play fa-5x"></i>';
- div.append(icon);
- return div[0].outerHTML;
+ const videoid = match[1]
+ if (!videoid) return
+ const div = $('<div class="youtube raw"></div>')
+ div.attr('data-videoid', videoid)
+ const thumbnailSrc = `//img.youtube.com/vi/${videoid}/hqdefault.jpg`
+ const image = `<img src="${thumbnailSrc}" />`
+ div.append(image)
+ const icon = '<i class="icon fa fa-youtube-play fa-5x"></i>'
+ div.append(icon)
+ return div[0].outerHTML
}
-);
-//vimeo
+)
+// vimeo
const vimeoPlugin = new Plugin(
// regexp to match
/{%vimeo\s*([\d\D]*?)\s*%}/,
(match, utils) => {
- const videoid = match[1];
- if (!videoid) return;
- const div = $('<div class="vimeo raw"></div>');
- div.attr('data-videoid', videoid);
- const icon = '<i class="icon fa fa-vimeo-square fa-5x"></i>';
- div.append(icon);
- return div[0].outerHTML;
+ const videoid = match[1]
+ if (!videoid) return
+ const div = $('<div class="vimeo raw"></div>')
+ div.attr('data-videoid', videoid)
+ const icon = '<i class="icon fa fa-vimeo-square fa-5x"></i>'
+ div.append(icon)
+ return div[0].outerHTML
}
-);
-//gist
+)
+// gist
const gistPlugin = new Plugin(
// regexp to match
/{%gist\s*([\d\D]*?)\s*%}/,
(match, utils) => {
- const gistid = match[1];
- const code = `<code data-gist-id="${gistid}"/>`;
- return code;
+ const gistid = match[1]
+ const code = `<code data-gist-id="${gistid}"/>`
+ return code
}
-);
-//TOC
+)
+// TOC
const tocPlugin = new Plugin(
// regexp to match
/^\[TOC\]$/i,
(match, utils) => '<div class="toc"></div>'
-);
-//slideshare
+)
+// slideshare
const slidesharePlugin = new Plugin(
// regexp to match
/{%slideshare\s*([\d\D]*?)\s*%}/,
(match, utils) => {
- const slideshareid = match[1];
- const div = $('<div class="slideshare raw"></div>');
- div.attr('data-slideshareid', slideshareid);
- return div[0].outerHTML;
+ const slideshareid = match[1]
+ const div = $('<div class="slideshare raw"></div>')
+ div.attr('data-slideshareid', slideshareid)
+ return div[0].outerHTML
}
-);
-//speakerdeck
+)
+// speakerdeck
const speakerdeckPlugin = new Plugin(
// regexp to match
/{%speakerdeck\s*([\d\D]*?)\s*%}/,
(match, utils) => {
- const speakerdeckid = match[1];
- const div = $('<div class="speakerdeck raw"></div>');
- div.attr('data-speakerdeckid', speakerdeckid);
- return div[0].outerHTML;
+ const speakerdeckid = match[1]
+ const div = $('<div class="speakerdeck raw"></div>')
+ div.attr('data-speakerdeckid', speakerdeckid)
+ return div[0].outerHTML
}
-);
-//pdf
+)
+// pdf
const pdfPlugin = new Plugin(
// regexp to match
/{%pdf\s*([\d\D]*?)\s*%}/,
(match, utils) => {
- const pdfurl = match[1];
- if (!isValidURL(pdfurl)) return match[0];
- const div = $('<div class="pdf raw"></div>');
- div.attr('data-pdfurl', pdfurl);
- return div[0].outerHTML;
+ const pdfurl = match[1]
+ if (!isValidURL(pdfurl)) return match[0]
+ const div = $('<div class="pdf raw"></div>')
+ div.attr('data-pdfurl', pdfurl)
+ return div[0].outerHTML
}
-);
+)
-//yaml meta, from https://github.com/eugeneware/remarkable-meta
-function get(state, line) {
- const pos = state.bMarks[line];
- const max = state.eMarks[line];
- return state.src.substr(pos, max - pos);
+// yaml meta, from https://github.com/eugeneware/remarkable-meta
+function get (state, line) {
+ const pos = state.bMarks[line]
+ const max = state.eMarks[line]
+ return state.src.substr(pos, max - pos)
}
-function meta(state, start, end, silent) {
- if (start !== 0 || state.blkIndent !== 0) return false;
- if (state.tShift[start] < 0) return false;
- if (!get(state, start).match(/^---$/)) return false;
-
- const data = [];
- for (var line = start + 1; line < end; line++) {
- const str = get(state, line);
- if (str.match(/^(\.{3}|-{3})$/)) break;
- if (state.tShift[line] < 0) break;
- data.push(str);
- }
-
- if (line >= end) return false;
-
- try {
- md.meta = jsyaml.safeLoad(data.join('\n')) || {};
- delete md.metaError;
- } catch(err) {
- md.metaError = err;
- console.warn(err);
- return false;
- }
-
- state.line = line + 1;
-
- return true;
+function meta (state, start, end, silent) {
+ if (start !== 0 || state.blkIndent !== 0) return false
+ if (state.tShift[start] < 0) return false
+ if (!get(state, start).match(/^---$/)) return false
+
+ const data = []
+ for (var line = start + 1; line < end; line++) {
+ const str = get(state, line)
+ if (str.match(/^(\.{3}|-{3})$/)) break
+ if (state.tShift[line] < 0) break
+ data.push(str)
+ }
+
+ if (line >= end) return false
+
+ try {
+ md.meta = window.jsyaml.safeLoad(data.join('\n')) || {}
+ delete md.metaError
+ } catch (err) {
+ md.metaError = err
+ console.warn(err)
+ return false
+ }
+
+ state.line = line + 1
+
+ return true
}
-function metaPlugin(md) {
- md.meta = md.meta || {};
- md.block.ruler.before('code', 'meta', meta, {
- alt: []
- });
+function metaPlugin (md) {
+ md.meta = md.meta || {}
+ md.block.ruler.before('code', 'meta', meta, {
+ alt: []
+ })
}
-md.use(metaPlugin);
-md.use(youtubePlugin);
-md.use(vimeoPlugin);
-md.use(gistPlugin);
-md.use(tocPlugin);
-md.use(slidesharePlugin);
-md.use(speakerdeckPlugin);
-md.use(pdfPlugin);
+md.use(metaPlugin)
+md.use(youtubePlugin)
+md.use(vimeoPlugin)
+md.use(gistPlugin)
+md.use(tocPlugin)
+md.use(slidesharePlugin)
+md.use(speakerdeckPlugin)
+md.use(pdfPlugin)
export default {
md
-};
+}
diff --git a/public/js/google-drive-picker.js b/public/js/google-drive-picker.js
index 94aa77ff..5006cd25 100644
--- a/public/js/google-drive-picker.js
+++ b/public/js/google-drive-picker.js
@@ -1,119 +1,118 @@
-/**!
+/** !
* Google Drive File Picker Example
* By Daniel Lo Nigro (http://dan.cx/)
*/
-(function() {
- /**
- * Initialise a Google Driver file picker
- */
- var FilePicker = window.FilePicker = function(options) {
- // Config
- this.apiKey = options.apiKey;
- this.clientId = options.clientId;
-
- // Elements
- this.buttonEl = options.buttonEl;
-
- // Events
- this.onSelect = options.onSelect;
- this.buttonEl.on('click', this.open.bind(this));
-
- // Disable the button until the API loads, as it won't work properly until then.
- this.buttonEl.prop('disabled', true);
+(function () {
+ /**
+ * Initialise a Google Driver file picker
+ */
+ var FilePicker = window.FilePicker = function (options) {
+ // Config
+ this.apiKey = options.apiKey
+ this.clientId = options.clientId
- // Load the drive API
- gapi.client.setApiKey(this.apiKey);
- gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this));
- google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) });
- }
+ // Elements
+ this.buttonEl = options.buttonEl
- FilePicker.prototype = {
- /**
- * Open the file picker.
- */
- open: function() {
- // Check if the user has already authenticated
- var token = gapi.auth.getToken();
- if (token) {
- this._showPicker();
- } else {
- // The user has not yet authenticated with Google
- // We need to do the authentication before displaying the Drive picker.
- this._doAuth(false, function() { this._showPicker(); }.bind(this));
- }
- },
-
- /**
- * Show the file picker once authentication has been done.
- * @private
- */
- _showPicker: function() {
- var accessToken = gapi.auth.getToken().access_token;
- var view = new google.picker.DocsView();
- view.setMimeTypes("text/markdown,text/html");
- view.setIncludeFolders(true);
- view.setOwnedByMe(true);
- this.picker = new google.picker.PickerBuilder().
- enableFeature(google.picker.Feature.NAV_HIDDEN).
- addView(view).
- setAppId(this.clientId).
- setOAuthToken(accessToken).
- setCallback(this._pickerCallback.bind(this)).
- build().
- setVisible(true);
- },
-
- /**
- * Called when a file has been selected in the Google Drive file picker.
- * @private
- */
- _pickerCallback: function(data) {
- if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
- var file = data[google.picker.Response.DOCUMENTS][0],
- id = file[google.picker.Document.ID],
- request = gapi.client.drive.files.get({
- fileId: id
- });
-
- request.execute(this._fileGetCallback.bind(this));
- }
- },
- /**
- * Called when file details have been retrieved from Google Drive.
- * @private
- */
- _fileGetCallback: function(file) {
- if (this.onSelect) {
- this.onSelect(file);
- }
- },
-
- /**
- * Called when the Google Drive file picker API has finished loading.
- * @private
- */
- _pickerApiLoaded: function() {
- this.buttonEl.prop('disabled', false);
- },
-
- /**
- * Called when the Google Drive API has finished loading.
- * @private
- */
- _driveApiLoaded: function() {
- this._doAuth(true);
- },
-
- /**
- * Authenticate with Google Drive via the Google JavaScript API.
- * @private
- */
- _doAuth: function(immediate, callback) {
- gapi.auth.authorize({
- client_id: this.clientId,
- scope: 'https://www.googleapis.com/auth/drive.readonly',
- immediate: immediate
- }, callback ? callback : function() {});
- }
- };
-}());
+ // Events
+ this.onSelect = options.onSelect
+ this.buttonEl.on('click', this.open.bind(this))
+
+ // Disable the button until the API loads, as it won't work properly until then.
+ this.buttonEl.prop('disabled', true)
+
+ // Load the drive API
+ window.gapi.client.setApiKey(this.apiKey)
+ window.gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this))
+ window.google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) })
+ }
+
+ FilePicker.prototype = {
+ /**
+ * Open the file picker.
+ */
+ open: function () {
+ // Check if the user has already authenticated
+ var token = window.gapi.auth.getToken()
+ if (token) {
+ this._showPicker()
+ } else {
+ // The user has not yet authenticated with Google
+ // We need to do the authentication before displaying the Drive picker.
+ this._doAuth(false, function () { this._showPicker() }.bind(this))
+ }
+ },
+
+ /**
+ * Show the file picker once authentication has been done.
+ * @private
+ */
+ _showPicker: function () {
+ var accessToken = window.gapi.auth.getToken().access_token
+ var view = new window.google.picker.DocsView()
+ view.setMimeTypes('text/markdown,text/html')
+ view.setIncludeFolders(true)
+ view.setOwnedByMe(true)
+ this.picker = new window.google.picker.PickerBuilder()
+ .enableFeature(window.google.picker.Feature.NAV_HIDDEN)
+ .addView(view)
+ .setAppId(this.clientId)
+ .setOAuthToken(accessToken)
+ .setCallback(this._pickerCallback.bind(this))
+ .build()
+ .setVisible(true)
+ },
+
+ /**
+ * Called when a file has been selected in the Google Drive file picker.
+ * @private
+ */
+ _pickerCallback: function (data) {
+ if (data[window.google.picker.Response.ACTION] === window.google.picker.Action.PICKED) {
+ var file = data[window.google.picker.Response.DOCUMENTS][0]
+ var id = file[window.google.picker.Document.ID]
+ var request = window.gapi.client.drive.files.get({
+ fileId: id
+ })
+ request.execute(this._fileGetCallback.bind(this))
+ }
+ },
+ /**
+ * Called when file details have been retrieved from Google Drive.
+ * @private
+ */
+ _fileGetCallback: function (file) {
+ if (this.onSelect) {
+ this.onSelect(file)
+ }
+ },
+
+ /**
+ * Called when the Google Drive file picker API has finished loading.
+ * @private
+ */
+ _pickerApiLoaded: function () {
+ this.buttonEl.prop('disabled', false)
+ },
+
+ /**
+ * Called when the Google Drive API has finished loading.
+ * @private
+ */
+ _driveApiLoaded: function () {
+ this._doAuth(true)
+ },
+
+ /**
+ * Authenticate with Google Drive via the Google JavaScript API.
+ * @private
+ */
+ _doAuth: function (immediate, callback) {
+ window.gapi.auth.authorize({
+ client_id: this.clientId,
+ scope: 'https://www.googleapis.com/auth/drive.readonly',
+ immediate: immediate
+ }, callback || function () {})
+ }
+ }
+}())
diff --git a/public/js/google-drive-upload.js b/public/js/google-drive-upload.js
index eabc5b7f..6c0e8a62 100644
--- a/public/js/google-drive-upload.js
+++ b/public/js/google-drive-upload.js
@@ -1,30 +1,31 @@
+/* eslint-env browser, jquery */
/**
* Helper for implementing retries with backoff. Initial retry
* delay is 1 second, increasing by 2x (+jitter) for subsequent retries
*
* @constructor
*/
-var RetryHandler = function() {
- this.interval = 1000; // Start at one second
- this.maxInterval = 60 * 1000; // Don't wait longer than a minute
-};
+var RetryHandler = function () {
+ this.interval = 1000 // Start at one second
+ this.maxInterval = 60 * 1000 // Don't wait longer than a minute
+}
/**
* Invoke the function after waiting
*
* @param {function} fn Function to invoke
*/
-RetryHandler.prototype.retry = function(fn) {
- setTimeout(fn, this.interval);
- this.interval = this.nextInterval_();
-};
+RetryHandler.prototype.retry = function (fn) {
+ setTimeout(fn, this.interval)
+ this.interval = this.nextInterval_()
+}
/**
* Reset the counter (e.g. after successful request.)
*/
-RetryHandler.prototype.reset = function() {
- this.interval = 1000;
-};
+RetryHandler.prototype.reset = function () {
+ this.interval = 1000
+}
/**
* Calculate the next wait time.
@@ -32,10 +33,10 @@ RetryHandler.prototype.reset = function() {
*
* @private
*/
-RetryHandler.prototype.nextInterval_ = function() {
- var interval = this.interval * 2 + this.getRandomInt_(0, 1000);
- return Math.min(interval, this.maxInterval);
-};
+RetryHandler.prototype.nextInterval_ = function () {
+ var interval = this.interval * 2 + this.getRandomInt_(0, 1000)
+ return Math.min(interval, this.maxInterval)
+}
/**
* Get a random int in the range of min to max. Used to add jitter to wait times.
@@ -44,10 +45,9 @@ RetryHandler.prototype.nextInterval_ = function() {
* @param {number} max Upper bounds
* @private
*/
-RetryHandler.prototype.getRandomInt_ = function(min, max) {
- return Math.floor(Math.random() * (max - min + 1) + min);
-};
-
+RetryHandler.prototype.getRandomInt_ = function (min, max) {
+ return Math.floor(Math.random() * (max - min + 1) + min)
+}
/**
* Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether
@@ -75,116 +75,115 @@ RetryHandler.prototype.getRandomInt_ = function(min, max) {
* @param {function} [options.onProgress] Callback for status for the in-progress upload
* @param {function} [options.onError] Callback if upload fails
*/
-var MediaUploader = function(options) {
- var noop = function() {};
- this.file = options.file;
- this.contentType = options.contentType || this.file.type || 'application/octet-stream';
+var MediaUploader = function (options) {
+ var noop = function () {}
+ this.file = options.file
+ this.contentType = options.contentType || this.file.type || 'application/octet-stream'
this.metadata = options.metadata || {
'title': this.file.name,
'mimeType': this.contentType
- };
- this.token = options.token;
- this.onComplete = options.onComplete || noop;
- this.onProgress = options.onProgress || noop;
- this.onError = options.onError || noop;
- this.offset = options.offset || 0;
- this.chunkSize = options.chunkSize || 0;
- this.retryHandler = new RetryHandler();
+ }
+ this.token = options.token
+ this.onComplete = options.onComplete || noop
+ this.onProgress = options.onProgress || noop
+ this.onError = options.onError || noop
+ this.offset = options.offset || 0
+ this.chunkSize = options.chunkSize || 0
+ this.retryHandler = new RetryHandler()
- this.url = options.url;
+ this.url = options.url
if (!this.url) {
- var params = options.params || {};
- params.uploadType = 'resumable';
- this.url = this.buildUrl_(options.fileId, params, options.baseUrl);
+ var params = options.params || {}
+ params.uploadType = 'resumable'
+ this.url = this.buildUrl_(options.fileId, params, options.baseUrl)
}
- this.httpMethod = options.fileId ? 'PUT' : 'POST';
-};
+ this.httpMethod = options.fileId ? 'PUT' : 'POST'
+}
/**
* Initiate the upload.
*/
-MediaUploader.prototype.upload = function() {
- var self = this;
- var xhr = new XMLHttpRequest();
+MediaUploader.prototype.upload = function () {
+ var xhr = new XMLHttpRequest()
- xhr.open(this.httpMethod, this.url, true);
- xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);
- xhr.setRequestHeader('Content-Type', 'application/json');
- xhr.setRequestHeader('X-Upload-Content-Length', this.file.size);
- xhr.setRequestHeader('X-Upload-Content-Type', this.contentType);
+ xhr.open(this.httpMethod, this.url, true)
+ xhr.setRequestHeader('Authorization', 'Bearer ' + this.token)
+ xhr.setRequestHeader('Content-Type', 'application/json')
+ xhr.setRequestHeader('X-Upload-Content-Length', this.file.size)
+ xhr.setRequestHeader('X-Upload-Content-Type', this.contentType)
- xhr.onload = function(e) {
+ xhr.onload = function (e) {
if (e.target.status < 400) {
- var location = e.target.getResponseHeader('Location');
- this.url = location;
- this.sendFile_();
+ var location = e.target.getResponseHeader('Location')
+ this.url = location
+ this.sendFile_()
} else {
- this.onUploadError_(e);
+ this.onUploadError_(e)
}
- }.bind(this);
- xhr.onerror = this.onUploadError_.bind(this);
- xhr.send(JSON.stringify(this.metadata));
-};
+ }.bind(this)
+ xhr.onerror = this.onUploadError_.bind(this)
+ xhr.send(JSON.stringify(this.metadata))
+}
/**
* Send the actual file content.
*
* @private
*/
-MediaUploader.prototype.sendFile_ = function() {
- var content = this.file;
- var end = this.file.size;
+MediaUploader.prototype.sendFile_ = function () {
+ var content = this.file
+ var end = this.file.size
if (this.offset || this.chunkSize) {
// Only bother to slice the file if we're either resuming or uploading in chunks
if (this.chunkSize) {
- end = Math.min(this.offset + this.chunkSize, this.file.size);
+ end = Math.min(this.offset + this.chunkSize, this.file.size)
}
- content = content.slice(this.offset, end);
+ content = content.slice(this.offset, end)
}
- var xhr = new XMLHttpRequest();
- xhr.open('PUT', this.url, true);
- xhr.setRequestHeader('Content-Type', this.contentType);
- xhr.setRequestHeader('Content-Range', "bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size);
- xhr.setRequestHeader('X-Upload-Content-Type', this.file.type);
+ var xhr = new XMLHttpRequest()
+ xhr.open('PUT', this.url, true)
+ xhr.setRequestHeader('Content-Type', this.contentType)
+ xhr.setRequestHeader('Content-Range', 'bytes ' + this.offset + '-' + (end - 1) + '/' + this.file.size)
+ xhr.setRequestHeader('X-Upload-Content-Type', this.file.type)
if (xhr.upload) {
- xhr.upload.addEventListener('progress', this.onProgress);
+ xhr.upload.addEventListener('progress', this.onProgress)
}
- xhr.onload = this.onContentUploadSuccess_.bind(this);
- xhr.onerror = this.onContentUploadError_.bind(this);
- xhr.send(content);
-};
+ xhr.onload = this.onContentUploadSuccess_.bind(this)
+ xhr.onerror = this.onContentUploadError_.bind(this)
+ xhr.send(content)
+}
/**
* Query for the state of the file for resumption.
*
* @private
*/
-MediaUploader.prototype.resume_ = function() {
- var xhr = new XMLHttpRequest();
- xhr.open('PUT', this.url, true);
- xhr.setRequestHeader('Content-Range', "bytes */" + this.file.size);
- xhr.setRequestHeader('X-Upload-Content-Type', this.file.type);
+MediaUploader.prototype.resume_ = function () {
+ var xhr = new XMLHttpRequest()
+ xhr.open('PUT', this.url, true)
+ xhr.setRequestHeader('Content-Range', 'bytes */' + this.file.size)
+ xhr.setRequestHeader('X-Upload-Content-Type', this.file.type)
if (xhr.upload) {
- xhr.upload.addEventListener('progress', this.onProgress);
+ xhr.upload.addEventListener('progress', this.onProgress)
}
- xhr.onload = this.onContentUploadSuccess_.bind(this);
- xhr.onerror = this.onContentUploadError_.bind(this);
- xhr.send();
-};
+ xhr.onload = this.onContentUploadSuccess_.bind(this)
+ xhr.onerror = this.onContentUploadError_.bind(this)
+ xhr.send()
+}
/**
* Extract the last saved range if available in the request.
*
* @param {XMLHttpRequest} xhr Request object
*/
-MediaUploader.prototype.extractRange_ = function(xhr) {
- var range = xhr.getResponseHeader('Range');
+MediaUploader.prototype.extractRange_ = function (xhr) {
+ var range = xhr.getResponseHeader('Range')
if (range) {
- this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1;
+ this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1
}
-};
+}
/**
* Handle successful responses for uploads. Depending on the context,
@@ -194,17 +193,17 @@ MediaUploader.prototype.extractRange_ = function(xhr) {
* @private
* @param {object} e XHR event
*/
-MediaUploader.prototype.onContentUploadSuccess_ = function(e) {
- if (e.target.status == 200 || e.target.status == 201) {
- this.onComplete(e.target.response);
- } else if (e.target.status == 308) {
- this.extractRange_(e.target);
- this.retryHandler.reset();
- this.sendFile_();
+MediaUploader.prototype.onContentUploadSuccess_ = function (e) {
+ if (e.target.status === 200 || e.target.status === 201) {
+ this.onComplete(e.target.response)
+ } else if (e.target.status === 308) {
+ this.extractRange_(e.target)
+ this.retryHandler.reset()
+ this.sendFile_()
} else {
- this.onContentUploadError_(e);
+ this.onContentUploadError_(e)
}
-};
+}
/**
* Handles errors for uploads. Either retries or aborts depending
@@ -213,13 +212,13 @@ MediaUploader.prototype.onContentUploadSuccess_ = function(e) {
* @private
* @param {object} e XHR event
*/
-MediaUploader.prototype.onContentUploadError_ = function(e) {
+MediaUploader.prototype.onContentUploadError_ = function (e) {
if (e.target.status && e.target.status < 500) {
- this.onError(e.target.response);
+ this.onError(e.target.response)
} else {
- this.retryHandler.retry(this.resume_.bind(this));
+ this.retryHandler.retry(this.resume_.bind(this))
}
-};
+}
/**
* Handles errors for the initial request.
@@ -227,9 +226,9 @@ MediaUploader.prototype.onContentUploadError_ = function(e) {
* @private
* @param {object} e XHR event
*/
-MediaUploader.prototype.onUploadError_ = function(e) {
- this.onError(e.target.response); // TODO - Retries for initial upload
-};
+MediaUploader.prototype.onUploadError_ = function (e) {
+ this.onError(e.target.response) // TODO - Retries for initial upload
+}
/**
* Construct a query string from a hash/object
@@ -238,12 +237,12 @@ MediaUploader.prototype.onUploadError_ = function(e) {
* @param {object} [params] Key/value pairs for query string
* @return {string} query string
*/
-MediaUploader.prototype.buildQuery_ = function(params) {
- params = params || {};
- return Object.keys(params).map(function(key) {
- return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
- }).join('&');
-};
+MediaUploader.prototype.buildQuery_ = function (params) {
+ params = params || {}
+ return Object.keys(params).map(function (key) {
+ return encodeURIComponent(key) + '=' + encodeURIComponent(params[key])
+ }).join('&')
+}
/**
* Build the drive upload URL
@@ -253,16 +252,16 @@ MediaUploader.prototype.buildQuery_ = function(params) {
* @param {object} [params] Query parameters
* @return {string} URL
*/
-MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) {
- var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/';
+MediaUploader.prototype.buildUrl_ = function (id, params, baseUrl) {
+ var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/'
if (id) {
- url += id;
+ url += id
}
- var query = this.buildQuery_(params);
+ var query = this.buildQuery_(params)
if (query) {
- url += '?' + query;
+ url += '?' + query
}
- return url;
-};
+ return url
+}
-window.MediaUploader = MediaUploader;
+window.MediaUploader = MediaUploader
diff --git a/public/js/history.js b/public/js/history.js
index 34b2cba7..e14b80d8 100644
--- a/public/js/history.js
+++ b/public/js/history.js
@@ -1,372 +1,328 @@
-import store from 'store';
-import S from 'string';
+/* eslint-env browser, jquery */
+/* global serverurl, Cookies, moment */
+
+import store from 'store'
+import S from 'string'
import {
checkIfAuth
-} from './lib/common/login';
+} from './lib/common/login'
import {
urlpath
-} from './lib/config';
+} from './lib/config'
-window.migrateHistoryFromTempCallback = null;
+window.migrateHistoryFromTempCallback = null
-migrateHistoryFromTemp();
+migrateHistoryFromTemp()
-function migrateHistoryFromTemp() {
- if (url('#tempid')) {
- $.get(`${serverurl}/temp`, {
- tempid: url('#tempid')
- })
- .done(data => {
- if (data && data.temp) {
- getStorageHistory(olddata => {
- if (!olddata || olddata.length == 0) {
- saveHistoryToStorage(JSON.parse(data.temp));
- }
- });
- }
- })
- .always(() => {
- let hash = location.hash.split('#')[1];
- hash = hash.split('&');
- for (let 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 migrateHistoryFromTemp () {
+ if (window.url('#tempid')) {
+ $.get(`${serverurl}/temp`, {
+ tempid: window.url('#tempid')
+ })
+ .done(data => {
+ if (data && data.temp) {
+ getStorageHistory(olddata => {
+ if (!olddata || olddata.length === 0) {
+ saveHistoryToStorage(JSON.parse(data.temp))
+ }
+ })
+ }
+ })
+ .always(() => {
+ let hash = location.hash.split('#')[1]
+ hash = hash.split('&')
+ for (let i = 0; i < hash.length; i++) {
+ if (hash[i].indexOf('tempid') === 0) {
+ hash.splice(i, 1)
+ i--
+ }
+ }
+ hash = hash.join('&')
+ location.hash = hash
+ if (window.migrateHistoryFromTempCallback) { window.migrateHistoryFromTempCallback() }
+ })
+ }
}
-export function saveHistory(notehistory) {
- checkIfAuth(
+export function saveHistory (notehistory) {
+ checkIfAuth(
() => {
- saveHistoryToServer(notehistory);
+ saveHistoryToServer(notehistory)
},
() => {
- saveHistoryToStorage(notehistory);
+ saveHistoryToStorage(notehistory)
}
- );
+ )
}
-function saveHistoryToStorage(notehistory) {
- if (store.enabled)
- store.set('notehistory', JSON.stringify(notehistory));
- else
- saveHistoryToCookie(notehistory);
+function saveHistoryToStorage (notehistory) {
+ if (store.enabled) { store.set('notehistory', JSON.stringify(notehistory)) } else { saveHistoryToCookie(notehistory) }
}
-function saveHistoryToCookie(notehistory) {
- Cookies.set('notehistory', notehistory, {
- expires: 365
- });
+function saveHistoryToCookie (notehistory) {
+ Cookies.set('notehistory', notehistory, {
+ expires: 365
+ })
}
-function saveHistoryToServer(notehistory) {
- $.post(`${serverurl}/history`, {
- history: JSON.stringify(notehistory)
- });
+function saveHistoryToServer (notehistory) {
+ $.post(`${serverurl}/history`, {
+ history: JSON.stringify(notehistory)
+ })
}
-function saveCookieHistoryToStorage(callback) {
- store.set('notehistory', Cookies.get('notehistory'));
- callback();
-}
-
-export function saveStorageHistoryToServer(callback) {
- const data = store.get('notehistory');
- if (data) {
- $.post(`${serverurl}/history`, {
- history: data
- })
- .done(data => {
- callback(data);
- });
- }
-}
-
-function saveCookieHistoryToServer(callback) {
+export function saveStorageHistoryToServer (callback) {
+ const data = store.get('notehistory')
+ if (data) {
$.post(`${serverurl}/history`, {
- history: Cookies.get('notehistory')
- })
- .done(data => {
- callback(data);
- });
+ history: data
+ })
+ .done(data => {
+ callback(data)
+ })
+ }
}
-export function clearDuplicatedHistory(notehistory) {
- const newnotehistory = [];
- for (let i = 0; i < notehistory.length; i++) {
- let found = false;
- for (let j = 0; j < newnotehistory.length; j++) {
- const id = notehistory[i].id.replace(/\=+$/, '');
- const newId = newnotehistory[j].id.replace(/\=+$/, '');
- if (id == newId || notehistory[i].id == newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) {
- const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'));
- const newTime = (typeof newnotehistory[i].time === 'number' ? moment(newnotehistory[i].time) : moment(newnotehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'));
- if(time >= newTime) {
- newnotehistory[j] = notehistory[i];
- }
- found = true;
- break;
- }
+export function clearDuplicatedHistory (notehistory) {
+ const newnotehistory = []
+ for (let i = 0; i < notehistory.length; i++) {
+ let found = false
+ for (let j = 0; j < newnotehistory.length; j++) {
+ const id = notehistory[i].id.replace(/=+$/, '')
+ const newId = newnotehistory[j].id.replace(/=+$/, '')
+ if (id === newId || notehistory[i].id === newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) {
+ const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'))
+ const newTime = (typeof newnotehistory[i].time === 'number' ? moment(newnotehistory[i].time) : moment(newnotehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'))
+ if (time >= newTime) {
+ newnotehistory[j] = notehistory[i]
}
- if (!found)
- newnotehistory.push(notehistory[i]);
+ found = true
+ break
+ }
}
- return newnotehistory;
+ if (!found) { newnotehistory.push(notehistory[i]) }
+ }
+ return newnotehistory
}
-function addHistory(id, text, time, tags, pinned, notehistory) {
+function addHistory (id, text, time, tags, pinned, notehistory) {
// only add when note id exists
- if (id) {
- notehistory.push({
- id,
- text,
- time,
- tags,
- pinned
- });
- }
- return notehistory;
+ if (id) {
+ notehistory.push({
+ id,
+ text,
+ time,
+ tags,
+ pinned
+ })
+ }
+ return notehistory
}
-export function removeHistory(id, notehistory) {
- for (let i = 0; i < notehistory.length; i++) {
- if (notehistory[i].id == id) {
- notehistory.splice(i, 1);
- i -= 1;
- }
+export function removeHistory (id, notehistory) {
+ for (let i = 0; i < notehistory.length; i++) {
+ if (notehistory[i].id === id) {
+ notehistory.splice(i, 1)
+ i -= 1
}
- return notehistory;
+ }
+ return notehistory
}
-//used for inner
-export function writeHistory(title, tags) {
- checkIfAuth(
+// used for inner
+export function writeHistory (title, tags) {
+ checkIfAuth(
() => {
// no need to do this anymore, this will count from server-side
// writeHistoryToServer(title, tags);
},
() => {
- writeHistoryToStorage(title, tags);
+ writeHistoryToStorage(title, tags)
}
- );
+ )
}
-function writeHistoryToServer(title, tags) {
- $.get(`${serverurl}/history`)
- .done(data => {
- try {
- if (data.history) {
- var notehistory = data.history;
- } else {
- var notehistory = [];
- }
- } catch (err) {
- var notehistory = [];
- }
- if (!notehistory)
- notehistory = [];
-
- const newnotehistory = generateHistory(title, tags, notehistory);
- saveHistoryToServer(newnotehistory);
- })
- .fail((xhr, status, error) => {
- console.error(xhr.responseText);
- });
+function writeHistoryToCookie (title, tags) {
+ var notehistory
+ try {
+ notehistory = Cookies.getJSON('notehistory')
+ } catch (err) {
+ notehistory = []
+ }
+ if (!notehistory) { notehistory = [] }
+ const newnotehistory = generateHistory(title, tags, notehistory)
+ saveHistoryToCookie(newnotehistory)
}
-function writeHistoryToCookie(title, tags) {
- try {
- var notehistory = Cookies.getJSON('notehistory');
- } catch (err) {
- var notehistory = [];
- }
- if (!notehistory)
- notehistory = [];
-
- const newnotehistory = generateHistory(title, tags, notehistory);
- saveHistoryToCookie(newnotehistory);
-}
-
-function writeHistoryToStorage(title, tags) {
- if (store.enabled) {
- let data = store.get('notehistory');
- if (data) {
- if (typeof data == "string")
- data = JSON.parse(data);
- var notehistory = data;
- } else
- var notehistory = [];
- if (!notehistory)
- notehistory = [];
-
- const newnotehistory = generateHistory(title, tags, notehistory);
- saveHistoryToStorage(newnotehistory);
+function writeHistoryToStorage (title, tags) {
+ if (store.enabled) {
+ let data = store.get('notehistory')
+ var notehistory
+ if (data) {
+ if (typeof data === 'string') { data = JSON.parse(data) }
+ notehistory = data
} else {
- writeHistoryToCookie(title, tags);
+ notehistory = []
}
+ if (!notehistory) { notehistory = [] }
+
+ const newnotehistory = generateHistory(title, tags, notehistory)
+ saveHistoryToStorage(newnotehistory)
+ } else {
+ writeHistoryToCookie(title, tags)
+ }
}
if (!Array.isArray) {
- Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]';
+ Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]'
}
-function renderHistory(title, tags) {
- //console.debug(tags);
- const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1];
- return {
- id,
- text: title,
- time: moment().valueOf(),
- tags
- };
+function renderHistory (title, tags) {
+ // console.debug(tags);
+ const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1]
+ return {
+ id,
+ text: title,
+ time: moment().valueOf(),
+ tags
+ }
}
-function generateHistory(title, tags, notehistory) {
- const info = renderHistory(title, tags);
- //keep any pinned data
- let pinned = false;
- for (let i = 0; i < notehistory.length; i++) {
- if (notehistory[i].id == info.id && notehistory[i].pinned) {
- pinned = true;
- break;
- }
+function generateHistory (title, tags, notehistory) {
+ const info = renderHistory(title, tags)
+ // keep any pinned data
+ let pinned = false
+ for (let i = 0; i < notehistory.length; i++) {
+ if (notehistory[i].id === info.id && notehistory[i].pinned) {
+ pinned = true
+ break
}
- notehistory = removeHistory(info.id, notehistory);
- notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory);
- notehistory = clearDuplicatedHistory(notehistory);
- return notehistory;
+ }
+ notehistory = removeHistory(info.id, notehistory)
+ notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory)
+ notehistory = clearDuplicatedHistory(notehistory)
+ return notehistory
}
-//used for outer
-export function getHistory(callback) {
- checkIfAuth(
+// used for outer
+export function getHistory (callback) {
+ checkIfAuth(
() => {
- getServerHistory(callback);
+ getServerHistory(callback)
},
() => {
- getStorageHistory(callback);
+ getStorageHistory(callback)
}
- );
+ )
}
-function getServerHistory(callback) {
- $.get(`${serverurl}/history`)
+function getServerHistory (callback) {
+ $.get(`${serverurl}/history`)
.done(data => {
- if (data.history) {
- callback(data.history);
- }
+ if (data.history) {
+ callback(data.history)
+ }
})
.fail((xhr, status, error) => {
- console.error(xhr.responseText);
- });
+ console.error(xhr.responseText)
+ })
}
-function getCookieHistory(callback) {
- callback(Cookies.getJSON('notehistory'));
+function getCookieHistory (callback) {
+ callback(Cookies.getJSON('notehistory'))
}
-export function getStorageHistory(callback) {
- if (store.enabled) {
- let data = store.get('notehistory');
- if (data) {
- if (typeof data == "string")
- data = JSON.parse(data);
- callback(data);
- } else
- getCookieHistory(callback);
- } else {
- getCookieHistory(callback);
- }
+export function getStorageHistory (callback) {
+ if (store.enabled) {
+ let data = store.get('notehistory')
+ if (data) {
+ if (typeof data === 'string') { data = JSON.parse(data) }
+ callback(data)
+ } else { getCookieHistory(callback) }
+ } else {
+ getCookieHistory(callback)
+ }
}
-export function parseHistory(list, callback) {
- checkIfAuth(
+export function parseHistory (list, callback) {
+ checkIfAuth(
() => {
- parseServerToHistory(list, callback);
+ parseServerToHistory(list, callback)
},
() => {
- parseStorageToHistory(list, callback);
+ parseStorageToHistory(list, callback)
}
- );
+ )
}
-export function parseServerToHistory(list, callback) {
- $.get(`${serverurl}/history`)
+export function parseServerToHistory (list, callback) {
+ $.get(`${serverurl}/history`)
.done(data => {
- if (data.history) {
- parseToHistory(list, data.history, callback);
- }
+ if (data.history) {
+ parseToHistory(list, data.history, callback)
+ }
})
.fail((xhr, status, error) => {
- console.error(xhr.responseText);
- });
+ console.error(xhr.responseText)
+ })
}
-function parseCookieToHistory(list, callback) {
- const notehistory = Cookies.getJSON('notehistory');
- parseToHistory(list, notehistory, callback);
+function parseCookieToHistory (list, callback) {
+ const notehistory = Cookies.getJSON('notehistory')
+ parseToHistory(list, notehistory, callback)
}
-export function parseStorageToHistory(list, callback) {
- if (store.enabled) {
- let 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);
- }
+export function parseStorageToHistory (list, callback) {
+ if (store.enabled) {
+ let 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 (let i = 0; i < notehistory.length; i++) {
- //parse time to timestamp and fromNow
- const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'));
- notehistory[i].timestamp = timestamp.valueOf();
- notehistory[i].fromNow = timestamp.fromNow();
- notehistory[i].time = timestamp.format('llll');
+function parseToHistory (list, notehistory, callback) {
+ if (!callback) return
+ else if (!list || !notehistory) callback(list, notehistory)
+ else if (notehistory && notehistory.length > 0) {
+ for (let i = 0; i < notehistory.length; i++) {
+ // parse time to timestamp and fromNow
+ const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'))
+ notehistory[i].timestamp = timestamp.valueOf()
+ notehistory[i].fromNow = timestamp.fromNow()
+ notehistory[i].time = timestamp.format('llll')
// prevent XSS
- notehistory[i].text = S(notehistory[i].text).escapeHTML().s;
- notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? S(notehistory[i].tags).escapeHTML().s.split(',') : [];
+ notehistory[i].text = S(notehistory[i].text).escapeHTML().s
+ notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? S(notehistory[i].tags).escapeHTML().s.split(',') : []
// add to list
- if (notehistory[i].id && list.get('id', notehistory[i].id).length == 0)
- list.add(notehistory[i]);
- }
+ if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) { list.add(notehistory[i]) }
}
- callback(list, notehistory);
+ }
+ callback(list, notehistory)
}
-export function postHistoryToServer(noteId, data, callback) {
- $.post(`${serverurl}/history/${noteId}`, data)
+export function postHistoryToServer (noteId, data, callback) {
+ $.post(`${serverurl}/history/${noteId}`, data)
.done(result => callback(null, result))
.fail((xhr, status, error) => {
- console.error(xhr.responseText);
- return callback(error, null);
- });
+ console.error(xhr.responseText)
+ return callback(error, null)
+ })
}
-export function deleteServerHistory(noteId, callback) {
- $.ajax({
- url: `${serverurl}/history${noteId ? '/' + noteId : ""}`,
- type: 'DELETE'
- })
+export function deleteServerHistory (noteId, callback) {
+ $.ajax({
+ url: `${serverurl}/history${noteId ? '/' + noteId : ''}`,
+ type: 'DELETE'
+ })
.done(result => callback(null, result))
.fail((xhr, status, error) => {
- console.error(xhr.responseText);
- return callback(error, null);
- });
+ console.error(xhr.responseText)
+ return callback(error, null)
+ })
}
diff --git a/public/js/htmlExport.js b/public/js/htmlExport.js
index 1c2c5eb9..1a873aca 100644
--- a/public/js/htmlExport.js
+++ b/public/js/htmlExport.js
@@ -1,6 +1,6 @@
-require('../css/github-extract.css');
-require('../css/markdown.css');
-require('../css/extra.css');
-require('../css/slide-preview.css');
-require('../css/google-font.css');
-require('../css/site.css');
+require('../css/github-extract.css')
+require('../css/markdown.css')
+require('../css/extra.css')
+require('../css/slide-preview.css')
+require('../css/google-font.css')
+require('../css/site.css')
diff --git a/public/js/index.js b/public/js/index.js
index f0c476ef..e672a68d 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -1,26 +1,30 @@
-/* jquery and jquery plugins */
-require('../vendor/showup/showup');
+/* eslint-env browser, jquery */
+/* global CodeMirror, Cookies, moment, editor, ui, Spinner,
+ modeType, Idle, serverurl, key, gapi, Dropbox, FilePicker
+ ot, MediaUploader, hex2rgb, num_loaded, Visibility */
-require('../css/index.css');
-require('../css/extra.css');
-require('../css/slide-preview.css');
-require('../css/site.css');
+require('../vendor/showup/showup')
-require('highlight.js/styles/github-gist.css');
+require('../css/index.css')
+require('../css/extra.css')
+require('../css/slide-preview.css')
+require('../css/site.css')
-var toMarkdown = require('to-markdown');
+require('highlight.js/styles/github-gist.css')
-var saveAs = require('file-saver').saveAs;
-var randomColor = require('randomcolor');
+var toMarkdown = require('to-markdown')
-var _ = require("lodash");
+var saveAs = require('file-saver').saveAs
+var randomColor = require('randomcolor')
-var List = require('list.js');
+var _ = require('lodash')
+
+var List = require('list.js')
import {
checkLoginStateChanged,
setloginStateChangeEvent
-} from './lib/common/login';
+} from './lib/common/login'
import {
debug,
@@ -31,7 +35,7 @@ import {
noteurl,
urlpath,
version
-} from './lib/config';
+} from './lib/config'
import {
autoLinkify,
@@ -53,14 +57,14 @@ import {
updateLastChange,
updateLastChangeUser,
updateOwner
-} from './extra';
+} from './extra'
import {
clearMap,
setupSyncAreas,
syncScrollToEdit,
syncScrollToView
-} from './syncscroll';
+} from './syncscroll'
import {
writeHistory,
@@ -68,4007 +72,3856 @@ import {
getHistory,
saveHistory,
removeHistory
-} from './history';
+} from './history'
-var renderer = require('./render');
-var preventXSS = renderer.preventXSS;
+var renderer = require('./render')
+var preventXSS = renderer.preventXSS
-var defaultTextHeight = 20;
-var viewportMargin = 20;
-var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
-var defaultEditorMode = 'gfm';
+var defaultTextHeight = 20
+var viewportMargin = 20
+var mac = CodeMirror.keyMap['default'] === CodeMirror.keyMap.macDefault
+var defaultEditorMode = 'gfm'
var defaultExtraKeys = {
- "F10": function (cm) {
- cm.setOption("fullScreen", !cm.getOption("fullScreen"));
- },
- "Esc": function (cm) {
- if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass;
- else if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
- },
- "Cmd-S": function () {
- return false;
- },
- "Ctrl-S": function () {
- return false;
- },
- "Enter": "newlineAndIndentContinueMarkdownList",
- "Tab": function (cm) {
- var tab = '\t';
- var spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" ");
- //auto indent whole line when in list or blockquote
- var cursor = cm.getCursor();
- var line = cm.getLine(cursor.line);
- var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/;
- var match;
- var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1;
- if (multiple) {
- cm.execCommand('defaultTab');
- } else if ((match = regex.exec(line)) !== null) {
- var ch = match[1].length;
- var pos = {
- line: cursor.line,
- ch: ch
- };
- if (cm.getOption('indentWithTabs'))
- cm.replaceRange(tab, pos, pos, '+input');
- else
- cm.replaceRange(spaces, pos, pos, '+input');
- } else {
- if (cm.getOption('indentWithTabs'))
- cm.execCommand('defaultTab');
- else {
- cm.replaceSelection(spaces);
- }
- }
- },
- "Cmd-Left": "goLineLeftSmart",
- "Cmd-Right": "goLineRight",
- "Ctrl-C": function (cm) {
- if (!mac && cm.getOption('keyMap').substr(0, 3) === 'vim') document.execCommand("copy");
- else return CodeMirror.Pass;
- },
- "Ctrl-*": function (cm) {
- wrapTextWith(cm, '*');
- },
- "Shift-Ctrl-8": function (cm) {
- wrapTextWith(cm, '*');
- },
- "Ctrl-_": function (cm) {
- wrapTextWith(cm, '_');
- },
- "Shift-Ctrl--": function (cm) {
- wrapTextWith(cm, '_');
- },
- "Ctrl-~": function (cm) {
- wrapTextWith(cm, '~');
- },
- "Shift-Ctrl-`": function (cm) {
- wrapTextWith(cm, '~');
- },
- "Ctrl-^": function (cm) {
- wrapTextWith(cm, '^');
- },
- "Shift-Ctrl-6": function (cm) {
- wrapTextWith(cm, '^');
- },
- "Ctrl-+": function (cm) {
- wrapTextWith(cm, '+');
- },
- "Shift-Ctrl-=": function (cm) {
- wrapTextWith(cm, '+');
- },
- "Ctrl-=": function (cm) {
- wrapTextWith(cm, '=');
- },
- "Shift-Ctrl-Backspace": function (cm) {
- wrapTextWith(cm, 'Backspace');
- }
-};
-
-var wrapSymbols = ['*', '_', '~', '^', '+', '='];
-
-function wrapTextWith(cm, symbol) {
- if (!cm.getSelection()) {
- return CodeMirror.Pass;
+ 'F10': function (cm) {
+ cm.setOption('fullScreen', !cm.getOption('fullScreen'))
+ },
+ 'Esc': function (cm) {
+ if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass
+ else if (cm.getOption('fullScreen')) cm.setOption('fullScreen', false)
+ },
+ 'Cmd-S': function () {
+ return false
+ },
+ 'Ctrl-S': function () {
+ return false
+ },
+ 'Enter': 'newlineAndIndentContinueMarkdownList',
+ 'Tab': function (cm) {
+ var tab = '\t'
+ var spaces = Array(parseInt(cm.getOption('indentUnit')) + 1).join(' ')
+ // auto indent whole line when in list or blockquote
+ var cursor = cm.getCursor()
+ var line = cm.getLine(cursor.line)
+ var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/
+ var match
+ var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1
+ if (multiple) {
+ cm.execCommand('defaultTab')
+ } else if ((match = regex.exec(line)) !== null) {
+ var ch = match[1].length
+ var pos = {
+ line: cursor.line,
+ ch: ch
+ }
+ if (cm.getOption('indentWithTabs')) { cm.replaceRange(tab, pos, pos, '+input') } else { cm.replaceRange(spaces, pos, pos, '+input') }
} else {
- var ranges = cm.listSelections();
- for (var i = 0; i < ranges.length; i++) {
- var range = ranges[i];
- if (!range.empty()) {
- var from = range.from(), to = range.to();
- if (symbol !== 'Backspace') {
- cm.replaceRange(symbol, to, to, '+input');
- cm.replaceRange(symbol, from, from, '+input');
+ if (cm.getOption('indentWithTabs')) { cm.execCommand('defaultTab') } else {
+ cm.replaceSelection(spaces)
+ }
+ }
+ },
+ 'Cmd-Left': 'goLineLeftSmart',
+ 'Cmd-Right': 'goLineRight',
+ 'Ctrl-C': function (cm) {
+ if (!mac && cm.getOption('keyMap').substr(0, 3) === 'vim') document.execCommand('copy')
+ else return CodeMirror.Pass
+ },
+ 'Ctrl-*': function (cm) {
+ wrapTextWith(cm, '*')
+ },
+ 'Shift-Ctrl-8': function (cm) {
+ wrapTextWith(cm, '*')
+ },
+ 'Ctrl-_': function (cm) {
+ wrapTextWith(cm, '_')
+ },
+ 'Shift-Ctrl--': function (cm) {
+ wrapTextWith(cm, '_')
+ },
+ 'Ctrl-~': function (cm) {
+ wrapTextWith(cm, '~')
+ },
+ 'Shift-Ctrl-`': function (cm) {
+ wrapTextWith(cm, '~')
+ },
+ 'Ctrl-^': function (cm) {
+ wrapTextWith(cm, '^')
+ },
+ 'Shift-Ctrl-6': function (cm) {
+ wrapTextWith(cm, '^')
+ },
+ 'Ctrl-+': function (cm) {
+ wrapTextWith(cm, '+')
+ },
+ 'Shift-Ctrl-=': function (cm) {
+ wrapTextWith(cm, '+')
+ },
+ 'Ctrl-=': function (cm) {
+ wrapTextWith(cm, '=')
+ },
+ 'Shift-Ctrl-Backspace': function (cm) {
+ wrapTextWith(cm, 'Backspace')
+ }
+}
+
+var wrapSymbols = ['*', '_', '~', '^', '+', '=']
+
+function wrapTextWith (cm, symbol) {
+ if (!cm.getSelection()) {
+ return CodeMirror.Pass
+ } else {
+ var ranges = cm.listSelections()
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i]
+ if (!range.empty()) {
+ var from = range.from()
+ var to = range.to()
+ if (symbol !== 'Backspace') {
+ cm.replaceRange(symbol, to, to, '+input')
+ cm.replaceRange(symbol, from, from, '+input')
// workaround selection range not correct after add symbol
- var _ranges = cm.listSelections();
- var anchorIndex = editor.indexFromPos(_ranges[i].anchor);
- var headIndex = editor.indexFromPos(_ranges[i].head);
- if (anchorIndex > headIndex) {
- _ranges[i].anchor.ch--;
- } else {
- _ranges[i].head.ch--;
- }
- cm.setSelections(_ranges);
- } else {
- var preEndPos = {
- line: to.line,
- ch: to.ch + 1
- };
- var preText = cm.getRange(to, preEndPos);
- var preIndex = wrapSymbols.indexOf(preText);
- var postEndPos = {
- line: from.line,
- ch: from.ch - 1
- };
- var postText = cm.getRange(postEndPos, from);
- var postIndex = wrapSymbols.indexOf(postText);
+ var _ranges = cm.listSelections()
+ var anchorIndex = window.editor.indexFromPos(_ranges[i].anchor)
+ var headIndex = window.editor.indexFromPos(_ranges[i].head)
+ if (anchorIndex > headIndex) {
+ _ranges[i].anchor.ch--
+ } else {
+ _ranges[i].head.ch--
+ }
+ cm.setSelections(_ranges)
+ } else {
+ var preEndPos = {
+ line: to.line,
+ ch: to.ch + 1
+ }
+ var preText = cm.getRange(to, preEndPos)
+ var preIndex = wrapSymbols.indexOf(preText)
+ var postEndPos = {
+ line: from.line,
+ ch: from.ch - 1
+ }
+ var postText = cm.getRange(postEndPos, from)
+ var postIndex = wrapSymbols.indexOf(postText)
// check if surround symbol are list in array and matched
- if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
- cm.replaceRange("", to, preEndPos, '+delete');
- cm.replaceRange("", postEndPos, from, '+delete');
- }
- }
- }
+ if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
+ cm.replaceRange('', to, preEndPos, '+delete')
+ cm.replaceRange('', postEndPos, from, '+delete')
+ }
}
+ }
}
+ }
}
-var idleTime = 300000; //5 mins
-var updateViewDebounce = 100;
-var cursorMenuThrottle = 50;
-var cursorActivityDebounce = 50;
-var cursorAnimatePeriod = 100;
-var supportContainers = ['success', 'info', 'warning', 'danger'];
-var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go'];
-var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid'];
+var idleTime = 300000 // 5 mins
+var updateViewDebounce = 100
+var cursorMenuThrottle = 50
+var cursorActivityDebounce = 50
+var cursorAnimatePeriod = 100
+var supportContainers = ['success', 'info', 'warning', 'danger']
+var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go']
+var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid']
var supportHeaders = [
- {
- text: '# h1',
- search: '#'
- },
- {
- text: '## h2',
- search: '##'
- },
- {
- text: '### h3',
- search: '###'
- },
- {
- text: '#### h4',
- search: '####'
- },
- {
- text: '##### h5',
- search: '#####'
- },
- {
- text: '###### h6',
- search: '######'
- },
- {
- text: '###### tags: `example`',
- search: '###### tags:'
- }
-];
+ {
+ text: '# h1',
+ search: '#'
+ },
+ {
+ text: '## h2',
+ search: '##'
+ },
+ {
+ text: '### h3',
+ search: '###'
+ },
+ {
+ text: '#### h4',
+ search: '####'
+ },
+ {
+ text: '##### h5',
+ search: '#####'
+ },
+ {
+ text: '###### h6',
+ search: '######'
+ },
+ {
+ text: '###### tags: `example`',
+ search: '###### tags:'
+ }
+]
var supportReferrals = [
- {
- text: '[reference link]',
- search: '[]'
- },
- {
- text: '[reference]: https:// "title"',
- search: '[]:'
- },
- {
- text: '[^footnote link]',
- search: '[^]'
- },
- {
- text: '[^footnote reference]: https:// "title"',
- search: '[^]:'
- },
- {
- text: '^[inline footnote]',
- search: '^[]'
- },
- {
- text: '[link text][reference]',
- search: '[][]'
- },
- {
- text: '[link text](https:// "title")',
- search: '[]()'
- },
- {
- text: '![image alt][reference]',
- search: '![][]'
- },
- {
- text: '![image alt](https:// "title")',
- search: '![]()'
- },
- {
- text: '![image alt](https:// "title" =WidthxHeight)',
- search: '![]()'
- },
- {
- text: '[TOC]',
- search: '[]'
- }
-];
+ {
+ text: '[reference link]',
+ search: '[]'
+ },
+ {
+ text: '[reference]: https:// "title"',
+ search: '[]:'
+ },
+ {
+ text: '[^footnote link]',
+ search: '[^]'
+ },
+ {
+ text: '[^footnote reference]: https:// "title"',
+ search: '[^]:'
+ },
+ {
+ text: '^[inline footnote]',
+ search: '^[]'
+ },
+ {
+ text: '[link text][reference]',
+ search: '[][]'
+ },
+ {
+ text: '[link text](https:// "title")',
+ search: '[]()'
+ },
+ {
+ text: '![image alt][reference]',
+ search: '![][]'
+ },
+ {
+ text: '![image alt](https:// "title")',
+ search: '![]()'
+ },
+ {
+ text: '![image alt](https:// "title" =WidthxHeight)',
+ search: '![]()'
+ },
+ {
+ text: '[TOC]',
+ search: '[]'
+ }
+]
var supportExternals = [
- {
- text: '{%youtube youtubeid %}',
- search: 'youtube'
- },
- {
- text: '{%vimeo vimeoid %}',
- search: 'vimeo'
- },
- {
- text: '{%gist gistid %}',
- search: 'gist'
- },
- {
- text: '{%slideshare slideshareid %}',
- search: 'slideshare'
- },
- {
- text: '{%speakerdeck speakerdeckid %}',
- search: 'speakerdeck'
- },
- {
- text: '{%pdf pdfurl %}',
- search: 'pdf'
- }
-];
+ {
+ text: '{%youtube youtubeid %}',
+ search: 'youtube'
+ },
+ {
+ text: '{%vimeo vimeoid %}',
+ search: 'vimeo'
+ },
+ {
+ text: '{%gist gistid %}',
+ search: 'gist'
+ },
+ {
+ text: '{%slideshare slideshareid %}',
+ search: 'slideshare'
+ },
+ {
+ text: '{%speakerdeck speakerdeckid %}',
+ search: 'speakerdeck'
+ },
+ {
+ text: '{%pdf pdfurl %}',
+ search: 'pdf'
+ }
+]
var supportExtraTags = [
- {
- text: '[name tag]',
- search: '[]',
- command: function () {
- return '[name=' + personalInfo.name + ']';
- },
- },
- {
- text: '[time tag]',
- search: '[]',
- command: function () {
- return '[time=' + moment().format('llll') + ']';
- },
- },
- {
- text: '[my color tag]',
- search: '[]',
- command: function () {
- return '[color=' + personalInfo.color + ']';
- }
- },
- {
- text: '[random color tag]',
- search: '[]',
- command: function () {
- var color = randomColor();
- return '[color=' + color + ']';
- }
- }
-];
+ {
+ text: '[name tag]',
+ search: '[]',
+ command: function () {
+ return '[name=' + window.personalInfo.name + ']'
+ }
+ },
+ {
+ text: '[time tag]',
+ search: '[]',
+ command: function () {
+ return '[time=' + moment().format('llll') + ']'
+ }
+ },
+ {
+ text: '[my color tag]',
+ search: '[]',
+ command: function () {
+ return '[color=' + window.personalInfo.color + ']'
+ }
+ },
+ {
+ text: '[random color tag]',
+ search: '[]',
+ command: function () {
+ var color = randomColor()
+ return '[color=' + color + ']'
+ }
+ }
+]
window.modeType = {
- edit: {
- name: "edit"
- },
- view: {
- name: "view"
- },
- both: {
- name: "both"
- }
-};
+ edit: {
+ name: 'edit'
+ },
+ view: {
+ name: 'view'
+ },
+ both: {
+ name: 'both'
+ }
+}
var statusType = {
- connected: {
- msg: "CONNECTED",
- label: "label-warning",
- fa: "fa-wifi"
- },
- online: {
- msg: "ONLINE",
- label: "label-primary",
- fa: "fa-users"
- },
- offline: {
- msg: "OFFLINE",
- label: "label-danger",
- fa: "fa-plug"
- }
-};
-var defaultMode = modeType.view;
-
-//global vars
-window.loaded = false;
-window.needRefresh = false;
-window.isDirty = false;
-window.editShown = false;
-window.visibleXS = false;
-window.visibleSM = false;
-window.visibleMD = false;
-window.visibleLG = false;
-window.isTouchDevice = 'ontouchstart' in document.documentElement;
-window.currentMode = defaultMode;
-window.currentStatus = statusType.offline;
+ connected: {
+ msg: 'CONNECTED',
+ label: 'label-warning',
+ fa: 'fa-wifi'
+ },
+ online: {
+ msg: 'ONLINE',
+ label: 'label-primary',
+ fa: 'fa-users'
+ },
+ offline: {
+ msg: 'OFFLINE',
+ label: 'label-danger',
+ fa: 'fa-plug'
+ }
+}
+var defaultMode = modeType.view
+
+// global vars
+window.loaded = false
+window.needRefresh = false
+window.isDirty = false
+window.editShown = false
+window.visibleXS = false
+window.visibleSM = false
+window.visibleMD = false
+window.visibleLG = false
+window.isTouchDevice = 'ontouchstart' in document.documentElement
+window.currentMode = defaultMode
+window.currentStatus = statusType.offline
window.lastInfo = {
- needRestore: false,
- cursor: null,
- scroll: null,
- edit: {
- scroll: {
- left: null,
- top: null
- },
- cursor: {
- line: null,
- ch: null
- },
- selections: null
+ needRestore: false,
+ cursor: null,
+ scroll: null,
+ edit: {
+ scroll: {
+ left: null,
+ top: null
},
- view: {
- scroll: {
- left: null,
- top: null
- }
+ cursor: {
+ line: null,
+ ch: null
},
- history: null
-};
-window.personalInfo = {};
-window.onlineUsers = [];
+ selections: null
+ },
+ view: {
+ scroll: {
+ left: null,
+ top: null
+ }
+ },
+ history: null
+}
+window.personalInfo = {}
+window.onlineUsers = []
window.fileTypes = {
- "pl": "perl",
- "cgi": "perl",
- "js": "javascript",
- "php": "php",
- "sh": "bash",
- "rb": "ruby",
- "html": "html",
- "py": "python"
-};
-
-//editor settings
-var textit = document.getElementById("textit");
-if (!textit) throw new Error("There was no textit area!");
+ 'pl': 'perl',
+ 'cgi': 'perl',
+ 'js': 'javascript',
+ 'php': 'php',
+ 'sh': 'bash',
+ 'rb': 'ruby',
+ 'html': 'html',
+ 'py': 'python'
+}
+
+// editor settings
+var textit = document.getElementById('textit')
+if (!textit) throw new Error('There was no textit area!')
window.editor = CodeMirror.fromTextArea(textit, {
- mode: defaultEditorMode,
- backdrop: defaultEditorMode,
- keyMap: "sublime",
- viewportMargin: viewportMargin,
- styleActiveLine: true,
- lineNumbers: true,
- lineWrapping: true,
- showCursorWhenSelecting: true,
- highlightSelectionMatches: true,
- indentUnit: 4,
- continueComments: "Enter",
- theme: "one-dark",
- inputStyle: "textarea",
- matchBrackets: true,
- autoCloseBrackets: true,
- matchTags: {
- bothTags: true
- },
- autoCloseTags: true,
- foldGutter: true,
- gutters: ["CodeMirror-linenumbers", "authorship-gutters", "CodeMirror-foldgutter"],
- extraKeys: defaultExtraKeys,
- flattenSpans: true,
- addModeClass: true,
- readOnly: true,
- autoRefresh: true,
- otherCursors: true,
- placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)"
-});
-var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor);
-defaultTextHeight = parseInt($(".CodeMirror").css('line-height'));
-
-var statusBarTemplate = null;
-var statusBar = null;
-var statusPanel = null;
-var statusCursor = null;
-var statusFile = null;
-var statusIndicators = null;
-var statusLength = null;
-var statusKeymap = null;
-var statusIndent = null;
-var statusTheme = null;
-var statusSpellcheck = null;
-var statusPreferences = null;
-
-function getStatusBarTemplate(callback) {
- $.get(serverurl + '/views/statusbar.html', function (template) {
- statusBarTemplate = template;
- if (callback) callback();
- });
-}
-getStatusBarTemplate();
-
-function addStatusBar() {
- if (!statusBarTemplate) {
- getStatusBarTemplate(addStatusBar);
- return;
- }
- statusBar = $(statusBarTemplate);
- statusCursor = statusBar.find('.status-cursor');
- statusFile = statusBar.find('.status-file');
- statusIndicators = statusBar.find('.status-indicators');
- statusIndent = statusBar.find('.status-indent');
- statusKeymap = statusBar.find('.status-keymap');
- statusLength = statusBar.find('.status-length');
- statusTheme = statusBar.find('.status-theme');
- statusSpellcheck = statusBar.find('.status-spellcheck');
- statusPreferences = statusBar.find('.status-preferences');
- statusPanel = editor.addPanel(statusBar[0], {
- position: "bottom"
- });
-
- setIndent();
- setKeymap();
- setTheme();
- setSpellcheck();
- setPreferences();
-}
-
-function setIndent() {
- var cookieIndentType = Cookies.get('indent_type');
- var cookieTabSize = parseInt(Cookies.get('tab_size'));
- var cookieSpaceUnits = parseInt(Cookies.get('space_units'));
- if (cookieIndentType) {
- if (cookieIndentType == 'tab') {
- editor.setOption('indentWithTabs', true);
- if (cookieTabSize)
- editor.setOption('indentUnit', cookieTabSize);
- } else if (cookieIndentType == 'space') {
- editor.setOption('indentWithTabs', false);
- if (cookieSpaceUnits)
- editor.setOption('indentUnit', cookieSpaceUnits);
- }
- }
- if (cookieTabSize)
- editor.setOption('tabSize', cookieTabSize);
-
- var type = statusIndicators.find('.indent-type');
- var widthLabel = statusIndicators.find('.indent-width-label');
- var widthInput = statusIndicators.find('.indent-width-input');
-
- function setType() {
- if (editor.getOption('indentWithTabs')) {
- Cookies.set('indent_type', 'tab', {
- expires: 365
- });
- type.text('Tab Size:');
- } else {
- Cookies.set('indent_type', 'space', {
- expires: 365
- });
- type.text('Spaces:');
- }
- }
- setType();
-
- function setUnit() {
- var unit = editor.getOption('indentUnit');
- if (editor.getOption('indentWithTabs')) {
- Cookies.set('tab_size', unit, {
- expires: 365
- });
- } else {
- Cookies.set('space_units', unit, {
- expires: 365
- });
- }
- widthLabel.text(unit);
+ mode: defaultEditorMode,
+ backdrop: defaultEditorMode,
+ keyMap: 'sublime',
+ viewportMargin: viewportMargin,
+ styleActiveLine: true,
+ lineNumbers: true,
+ lineWrapping: true,
+ showCursorWhenSelecting: true,
+ highlightSelectionMatches: true,
+ indentUnit: 4,
+ continueComments: 'Enter',
+ theme: 'one-dark',
+ inputStyle: 'textarea',
+ matchBrackets: true,
+ autoCloseBrackets: true,
+ matchTags: {
+ bothTags: true
+ },
+ autoCloseTags: true,
+ foldGutter: true,
+ gutters: ['CodeMirror-linenumbers', 'authorship-gutters', 'CodeMirror-foldgutter'],
+ extraKeys: defaultExtraKeys,
+ flattenSpans: true,
+ addModeClass: true,
+ readOnly: true,
+ autoRefresh: true,
+ otherCursors: true,
+ placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)"
+})
+var inlineAttach = window.inlineAttachment.editors.codemirror4.attach(editor)
+defaultTextHeight = parseInt($('.CodeMirror').css('line-height'))
+
+var statusBarTemplate = null
+var statusBar = null
+var statusCursor = null
+var statusFile = null
+var statusIndicators = null
+var statusLength = null
+var statusTheme = null
+var statusSpellcheck = null
+
+function getStatusBarTemplate (callback) {
+ $.get(serverurl + '/views/statusbar.html', function (template) {
+ statusBarTemplate = template
+ if (callback) callback()
+ })
+}
+getStatusBarTemplate()
+
+function addStatusBar () {
+ if (!statusBarTemplate) {
+ getStatusBarTemplate(addStatusBar)
+ return
+ }
+ statusBar = $(statusBarTemplate)
+ statusCursor = statusBar.find('.status-cursor')
+ statusFile = statusBar.find('.status-file')
+ statusIndicators = statusBar.find('.status-indicators')
+ statusBar.find('.status-indent')
+ statusBar.find('.status-keymap')
+ statusLength = statusBar.find('.status-length')
+ statusTheme = statusBar.find('.status-theme')
+ statusSpellcheck = statusBar.find('.status-spellcheck')
+ statusBar.find('.status-preferences')
+ editor.addPanel(statusBar[0], {
+ position: 'bottom'
+ })
+
+ setIndent()
+ setKeymap()
+ setTheme()
+ setSpellcheck()
+ setPreferences()
+}
+
+function setIndent () {
+ var cookieIndentType = Cookies.get('indent_type')
+ var cookieTabSize = parseInt(Cookies.get('tab_size'))
+ var cookieSpaceUnits = parseInt(Cookies.get('space_units'))
+ if (cookieIndentType) {
+ if (cookieIndentType === 'tab') {
+ editor.setOption('indentWithTabs', true)
+ if (cookieTabSize) { editor.setOption('indentUnit', cookieTabSize) }
+ } else if (cookieIndentType === 'space') {
+ editor.setOption('indentWithTabs', false)
+ if (cookieSpaceUnits) { editor.setOption('indentUnit', cookieSpaceUnits) }
+ }
+ }
+ if (cookieTabSize) { editor.setOption('tabSize', cookieTabSize) }
+
+ var type = statusIndicators.find('.indent-type')
+ var widthLabel = statusIndicators.find('.indent-width-label')
+ var widthInput = statusIndicators.find('.indent-width-input')
+
+ function setType () {
+ if (editor.getOption('indentWithTabs')) {
+ Cookies.set('indent_type', 'tab', {
+ expires: 365
+ })
+ type.text('Tab Size:')
+ } else {
+ Cookies.set('indent_type', 'space', {
+ expires: 365
+ })
+ type.text('Spaces:')
+ }
+ }
+ setType()
+
+ function setUnit () {
+ var unit = editor.getOption('indentUnit')
+ if (editor.getOption('indentWithTabs')) {
+ Cookies.set('tab_size', unit, {
+ expires: 365
+ })
+ } else {
+ Cookies.set('space_units', unit, {
+ expires: 365
+ })
+ }
+ widthLabel.text(unit)
+ }
+ setUnit()
+
+ type.click(function () {
+ if (editor.getOption('indentWithTabs')) {
+ editor.setOption('indentWithTabs', false)
+ cookieSpaceUnits = parseInt(Cookies.get('space_units'))
+ if (cookieSpaceUnits) { editor.setOption('indentUnit', cookieSpaceUnits) }
+ } else {
+ editor.setOption('indentWithTabs', true)
+ cookieTabSize = parseInt(Cookies.get('tab_size'))
+ if (cookieTabSize) {
+ editor.setOption('indentUnit', cookieTabSize)
+ editor.setOption('tabSize', cookieTabSize)
+ }
+ }
+ setType()
+ setUnit()
+ })
+ widthLabel.click(function () {
+ if (widthLabel.is(':visible')) {
+ widthLabel.addClass('hidden')
+ widthInput.removeClass('hidden')
+ widthInput.val(editor.getOption('indentUnit'))
+ widthInput.select()
+ } else {
+ widthLabel.removeClass('hidden')
+ widthInput.addClass('hidden')
+ }
+ })
+ widthInput.on('change', function () {
+ var val = parseInt(widthInput.val())
+ if (!val) val = editor.getOption('indentUnit')
+ if (val < 1) val = 1
+ else if (val > 10) val = 10
+
+ if (editor.getOption('indentWithTabs')) {
+ editor.setOption('tabSize', val)
+ }
+ editor.setOption('indentUnit', val)
+ setUnit()
+ })
+ widthInput.on('blur', function () {
+ widthLabel.removeClass('hidden')
+ widthInput.addClass('hidden')
+ })
+}
+
+function setKeymap () {
+ var cookieKeymap = Cookies.get('keymap')
+ if (cookieKeymap) { editor.setOption('keyMap', cookieKeymap) }
+
+ var label = statusIndicators.find('.ui-keymap-label')
+ var sublime = statusIndicators.find('.ui-keymap-sublime')
+ var emacs = statusIndicators.find('.ui-keymap-emacs')
+ var vim = statusIndicators.find('.ui-keymap-vim')
+
+ function setKeymapLabel () {
+ var keymap = editor.getOption('keyMap')
+ Cookies.set('keymap', keymap, {
+ expires: 365
+ })
+ label.text(keymap)
+ restoreOverrideEditorKeymap()
+ setOverrideBrowserKeymap()
+ }
+ setKeymapLabel()
+
+ sublime.click(function () {
+ editor.setOption('keyMap', 'sublime')
+ setKeymapLabel()
+ })
+ emacs.click(function () {
+ editor.setOption('keyMap', 'emacs')
+ setKeymapLabel()
+ })
+ vim.click(function () {
+ editor.setOption('keyMap', 'vim')
+ setKeymapLabel()
+ })
+}
+
+function setTheme () {
+ var cookieTheme = Cookies.get('theme')
+ if (cookieTheme) {
+ editor.setOption('theme', cookieTheme)
+ }
+
+ var themeToggle = statusTheme.find('.ui-theme-toggle')
+ themeToggle.click(function () {
+ var theme = editor.getOption('theme')
+ if (theme === 'one-dark') {
+ theme = 'default'
+ } else {
+ theme = 'one-dark'
}
- setUnit();
-
- type.click(function () {
- if (editor.getOption('indentWithTabs')) {
- editor.setOption('indentWithTabs', false);
- cookieSpaceUnits = parseInt(Cookies.get('space_units'));
- if (cookieSpaceUnits)
- editor.setOption('indentUnit', cookieSpaceUnits)
- } else {
- editor.setOption('indentWithTabs', true);
- cookieTabSize = parseInt(Cookies.get('tab_size'));
- if (cookieTabSize) {
- editor.setOption('indentUnit', cookieTabSize);
- editor.setOption('tabSize', cookieTabSize);
- }
- }
- setType();
- setUnit();
- });
- widthLabel.click(function () {
- if (widthLabel.is(':visible')) {
- widthLabel.addClass('hidden');
- widthInput.removeClass('hidden');
- widthInput.val(editor.getOption('indentUnit'));
- widthInput.select();
- } else {
- widthLabel.removeClass('hidden');
- widthInput.addClass('hidden');
- }
- });
- widthInput.on('change', function () {
- var val = parseInt(widthInput.val());
- if (!val) val = editor.getOption('indentUnit');
- if (val < 1) val = 1;
- else if (val > 10) val = 10;
-
- if (editor.getOption('indentWithTabs')) {
- editor.setOption('tabSize', val);
- }
- editor.setOption('indentUnit', val);
- setUnit();
- });
- widthInput.on('blur', function () {
- widthLabel.removeClass('hidden');
- widthInput.addClass('hidden');
- });
-}
-
-function setKeymap() {
- var cookieKeymap = Cookies.get('keymap');
- if (cookieKeymap)
- editor.setOption('keyMap', cookieKeymap);
-
- var label = statusIndicators.find('.ui-keymap-label');
- var sublime = statusIndicators.find('.ui-keymap-sublime');
- var emacs = statusIndicators.find('.ui-keymap-emacs');
- var vim = statusIndicators.find('.ui-keymap-vim');
-
- function setKeymapLabel() {
- var keymap = editor.getOption('keyMap');
- Cookies.set('keymap', keymap, {
- expires: 365
- });
- label.text(keymap);
- restoreOverrideEditorKeymap();
- setOverrideBrowserKeymap();
- }
- setKeymapLabel();
-
- sublime.click(function () {
- editor.setOption('keyMap', 'sublime');
- setKeymapLabel();
- });
- emacs.click(function () {
- editor.setOption('keyMap', 'emacs');
- setKeymapLabel();
- });
- vim.click(function () {
- editor.setOption('keyMap', 'vim');
- setKeymapLabel();
- });
-}
-
-function setTheme() {
- var cookieTheme = Cookies.get('theme');
- if (cookieTheme) {
- editor.setOption('theme', cookieTheme);
- }
-
- var themeToggle = statusTheme.find('.ui-theme-toggle');
- themeToggle.click(function () {
- var theme = editor.getOption('theme');
- if (theme == "one-dark") {
- theme = "default";
- } else {
- theme = "one-dark";
- }
- editor.setOption('theme', theme);
- Cookies.set('theme', theme, {
- expires: 365
- });
- checkTheme();
- });
- function checkTheme() {
- var theme = editor.getOption('theme');
- if (theme == "one-dark") {
- themeToggle.removeClass('active');
- } else {
- themeToggle.addClass('active');
- }
+ editor.setOption('theme', theme)
+ Cookies.set('theme', theme, {
+ expires: 365
+ })
+ checkTheme()
+ })
+ function checkTheme () {
+ var theme = editor.getOption('theme')
+ if (theme === 'one-dark') {
+ themeToggle.removeClass('active')
+ } else {
+ themeToggle.addClass('active')
}
- checkTheme();
+ }
+ checkTheme()
}
-function setSpellcheck() {
- var cookieSpellcheck = Cookies.get('spellcheck');
- if (cookieSpellcheck) {
- var mode = null;
- if (cookieSpellcheck === 'true' || cookieSpellcheck === true) {
- mode = 'spell-checker';
- } else {
- mode = defaultEditorMode;
- }
- if (mode && mode !== editor.getOption('mode')) {
- editor.setOption('mode', mode);
- }
- }
-
- var spellcheckToggle = statusSpellcheck.find('.ui-spellcheck-toggle');
- spellcheckToggle.click(function () {
- var mode = editor.getOption('mode');
- if (mode == defaultEditorMode) {
- mode = "spell-checker";
- } else {
- mode = defaultEditorMode;
- }
- if (mode && mode !== editor.getOption('mode')) {
- editor.setOption('mode', mode);
- }
- Cookies.set('spellcheck', (mode == "spell-checker"), {
- expires: 365
- });
- checkSpellcheck();
- });
- function checkSpellcheck() {
- var mode = editor.getOption('mode');
- if (mode == defaultEditorMode) {
- spellcheckToggle.removeClass('active');
- } else {
- spellcheckToggle.addClass('active');
- }
+function setSpellcheck () {
+ var cookieSpellcheck = Cookies.get('spellcheck')
+ if (cookieSpellcheck) {
+ var mode = null
+ if (cookieSpellcheck === 'true' || cookieSpellcheck === true) {
+ mode = 'spell-checker'
+ } else {
+ mode = defaultEditorMode
}
- checkSpellcheck();
-
- //workaround spellcheck might not activate beacuse the ajax loading
- if (num_loaded < 2) {
- var spellcheckTimer = setInterval(function () {
- if (num_loaded >= 2) {
- if (editor.getOption('mode') == "spell-checker")
- editor.setOption('mode', "spell-checker");
- clearInterval(spellcheckTimer);
- }
- }, 100);
+ if (mode && mode !== editor.getOption('mode')) {
+ editor.setOption('mode', mode)
}
-}
+ }
-var jumpToAddressBarKeymapName = mac ? "Cmd-L" : "Ctrl-L";
-var jumpToAddressBarKeymapValue = null;
-function resetEditorKeymapToBrowserKeymap() {
- var keymap = editor.getOption('keyMap');
- if (!jumpToAddressBarKeymapValue) {
- jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName];
- delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName];
- }
-}
-function restoreOverrideEditorKeymap() {
- var keymap = editor.getOption('keyMap');
- if (jumpToAddressBarKeymapValue) {
- CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = jumpToAddressBarKeymapValue;
- jumpToAddressBarKeymapValue = null;
- }
-}
-function setOverrideBrowserKeymap() {
- var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]');
- if(overrideBrowserKeymap.is(":checked")) {
- Cookies.set('preferences-override-browser-keymap', true, {
- expires: 365
- });
- restoreOverrideEditorKeymap();
+ var spellcheckToggle = statusSpellcheck.find('.ui-spellcheck-toggle')
+ spellcheckToggle.click(function () {
+ var mode = editor.getOption('mode')
+ if (mode === defaultEditorMode) {
+ mode = 'spell-checker'
} else {
- Cookies.remove('preferences-override-browser-keymap');
- resetEditorKeymapToBrowserKeymap();
+ mode = defaultEditorMode
}
-}
-
-function setPreferences() {
- var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]');
- var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap');
- if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === "true") {
- overrideBrowserKeymap.prop('checked', true);
- } else {
- overrideBrowserKeymap.prop('checked', false);
+ if (mode && mode !== editor.getOption('mode')) {
+ editor.setOption('mode', mode)
}
- setOverrideBrowserKeymap();
-
- overrideBrowserKeymap.change(function() {
- setOverrideBrowserKeymap();
- });
-}
-
-var selection = null;
-
-function updateStatusBar() {
- if (!statusBar) return;
- var cursor = editor.getCursor();
- var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1);
- if (selection) {
- var anchor = selection.anchor;
- var head = selection.head;
- var start = head.line <= anchor.line ? head : anchor;
- var end = head.line >= anchor.line ? head : anchor;
- var selectionText = ' — Selected ';
- var selectionCharCount = Math.abs(head.ch - anchor.ch);
- // borrow from brackets EditorStatusBar.js
- if (start.line !== end.line) {
- var lines = end.line - start.line + 1;
- if (end.ch === 0) {
- lines--;
- }
- selectionText += lines + ' lines';
- } else if (selectionCharCount > 0)
- selectionText += selectionCharCount + ' columns';
- if (start.line !== end.line || selectionCharCount > 0)
- cursorText += selectionText;
- }
- statusCursor.text(cursorText);
- var fileText = ' — ' + editor.lineCount() + ' Lines';
- statusFile.text(fileText);
- var docLength = editor.getValue().length;
- statusLength.text('Length ' + docLength);
- if (docLength > (docmaxlength * 0.95)) {
- statusLength.css('color', 'red');
- statusLength.attr('title', 'Your almost reach note max length limit.');
- } else if (docLength > (docmaxlength * 0.8)) {
- statusLength.css('color', 'orange');
- statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.');
+ Cookies.set('spellcheck', (mode === 'spell-checker'), {
+ expires: 365
+ })
+ checkSpellcheck()
+ })
+ function checkSpellcheck () {
+ var mode = editor.getOption('mode')
+ if (mode === defaultEditorMode) {
+ spellcheckToggle.removeClass('active')
} else {
- statusLength.css('color', 'white');
- statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.');
- }
-}
-
-//ui vars
+ spellcheckToggle.addClass('active')
+ }
+ }
+ checkSpellcheck()
+
+ // workaround spellcheck might not activate beacuse the ajax loading
+ /* eslint-disable camelcase */
+ if (num_loaded < 2) {
+ var spellcheckTimer = setInterval(function () {
+ if (num_loaded >= 2) {
+ if (editor.getOption('mode') === 'spell-checker') { editor.setOption('mode', 'spell-checker') }
+ clearInterval(spellcheckTimer)
+ }
+ }, 100)
+ }
+ /* eslint-endable camelcase */
+}
+
+var jumpToAddressBarKeymapName = mac ? 'Cmd-L' : 'Ctrl-L'
+var jumpToAddressBarKeymapValue = null
+function resetEditorKeymapToBrowserKeymap () {
+ var keymap = editor.getOption('keyMap')
+ if (!jumpToAddressBarKeymapValue) {
+ jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
+ delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
+ }
+}
+function restoreOverrideEditorKeymap () {
+ var keymap = editor.getOption('keyMap')
+ if (jumpToAddressBarKeymapValue) {
+ CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = jumpToAddressBarKeymapValue
+ jumpToAddressBarKeymapValue = null
+ }
+}
+function setOverrideBrowserKeymap () {
+ var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]')
+ if (overrideBrowserKeymap.is(':checked')) {
+ Cookies.set('preferences-override-browser-keymap', true, {
+ expires: 365
+ })
+ restoreOverrideEditorKeymap()
+ } else {
+ Cookies.remove('preferences-override-browser-keymap')
+ resetEditorKeymapToBrowserKeymap()
+ }
+}
+
+function setPreferences () {
+ var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]')
+ var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap')
+ if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') {
+ overrideBrowserKeymap.prop('checked', true)
+ } else {
+ overrideBrowserKeymap.prop('checked', false)
+ }
+ setOverrideBrowserKeymap()
+
+ overrideBrowserKeymap.change(function () {
+ setOverrideBrowserKeymap()
+ })
+}
+
+var selection = null
+
+function updateStatusBar () {
+ if (!statusBar) return
+ var cursor = editor.getCursor()
+ var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1)
+ if (selection) {
+ var anchor = selection.anchor
+ var head = selection.head
+ var start = head.line <= anchor.line ? head : anchor
+ var end = head.line >= anchor.line ? head : anchor
+ var selectionText = ' — Selected '
+ var selectionCharCount = Math.abs(head.ch - anchor.ch)
+ // borrow from brackets EditorStatusBar.js
+ if (start.line !== end.line) {
+ var lines = end.line - start.line + 1
+ if (end.ch === 0) {
+ lines--
+ }
+ selectionText += lines + ' lines'
+ } else if (selectionCharCount > 0) { selectionText += selectionCharCount + ' columns' }
+ if (start.line !== end.line || selectionCharCount > 0) { cursorText += selectionText }
+ }
+ statusCursor.text(cursorText)
+ var fileText = ' — ' + editor.lineCount() + ' Lines'
+ statusFile.text(fileText)
+ var docLength = editor.getValue().length
+ statusLength.text('Length ' + docLength)
+ if (docLength > (docmaxlength * 0.95)) {
+ statusLength.css('color', 'red')
+ statusLength.attr('title', 'Your almost reach note max length limit.')
+ } else if (docLength > (docmaxlength * 0.8)) {
+ statusLength.css('color', 'orange')
+ statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.')
+ } else {
+ statusLength.css('color', 'white')
+ statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.')
+ }
+}
+
+// ui vars
window.ui = {
- spinner: $(".ui-spinner"),
- content: $(".ui-content"),
- toolbar: {
- shortStatus: $(".ui-short-status"),
- status: $(".ui-status"),
- new: $(".ui-new"),
- publish: $(".ui-publish"),
- extra: {
- revision: $(".ui-extra-revision"),
- slide: $(".ui-extra-slide")
- },
- download: {
- markdown: $(".ui-download-markdown"),
- html: $(".ui-download-html"),
- rawhtml: $(".ui-download-raw-html"),
- pdf: $(".ui-download-pdf-beta"),
- },
- export: {
- dropbox: $(".ui-save-dropbox"),
- googleDrive: $(".ui-save-google-drive"),
- gist: $(".ui-save-gist"),
- snippet: $(".ui-save-snippet")
- },
- import: {
- dropbox: $(".ui-import-dropbox"),
- googleDrive: $(".ui-import-google-drive"),
- gist: $(".ui-import-gist"),
- snippet: $(".ui-import-snippet"),
- clipboard: $(".ui-import-clipboard")
- },
- mode: $(".ui-mode"),
- edit: $(".ui-edit"),
- view: $(".ui-view"),
- both: $(".ui-both"),
- uploadImage: $(".ui-upload-image")
+ spinner: $('.ui-spinner'),
+ content: $('.ui-content'),
+ toolbar: {
+ shortStatus: $('.ui-short-status'),
+ status: $('.ui-status'),
+ new: $('.ui-new'),
+ publish: $('.ui-publish'),
+ extra: {
+ revision: $('.ui-extra-revision'),
+ slide: $('.ui-extra-slide')
},
- infobar: {
- lastchange: $(".ui-lastchange"),
- lastchangeuser: $(".ui-lastchangeuser"),
- nolastchangeuser: $(".ui-no-lastchangeuser"),
- permission: {
- permission: $(".ui-permission"),
- label: $(".ui-permission-label"),
- freely: $(".ui-permission-freely"),
- editable: $(".ui-permission-editable"),
- locked: $(".ui-permission-locked"),
- private: $(".ui-permission-private"),
- limited: $(".ui-permission-limited"),
- protected: $(".ui-permission-protected")
- },
- delete: $(".ui-delete-note")
+ download: {
+ markdown: $('.ui-download-markdown'),
+ html: $('.ui-download-html'),
+ rawhtml: $('.ui-download-raw-html'),
+ pdf: $('.ui-download-pdf-beta')
},
- toc: {
- toc: $('.ui-toc'),
- affix: $('.ui-affix-toc'),
- label: $('.ui-toc-label'),
- dropdown: $('.ui-toc-dropdown')
+ export: {
+ dropbox: $('.ui-save-dropbox'),
+ googleDrive: $('.ui-save-google-drive'),
+ gist: $('.ui-save-gist'),
+ snippet: $('.ui-save-snippet')
},
- area: {
- edit: $(".ui-edit-area"),
- view: $(".ui-view-area"),
- codemirror: $(".ui-edit-area .CodeMirror"),
- codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"),
- codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"),
- codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"),
- markdown: $(".ui-view-area .markdown-body"),
- resize: {
- handle: $('.ui-resizable-handle'),
- syncToggle: $('.ui-sync-toggle')
- }
+ import: {
+ dropbox: $('.ui-import-dropbox'),
+ googleDrive: $('.ui-import-google-drive'),
+ gist: $('.ui-import-gist'),
+ snippet: $('.ui-import-snippet'),
+ clipboard: $('.ui-import-clipboard')
},
- modal: {
- snippetImportProjects: $("#snippetImportModalProjects"),
- snippetImportSnippets: $("#snippetImportModalSnippets"),
- revision: $("#revisionModal")
- }
-};
-
-//page actions
+ mode: $('.ui-mode'),
+ edit: $('.ui-edit'),
+ view: $('.ui-view'),
+ both: $('.ui-both'),
+ uploadImage: $('.ui-upload-image')
+ },
+ infobar: {
+ lastchange: $('.ui-lastchange'),
+ lastchangeuser: $('.ui-lastchangeuser'),
+ nolastchangeuser: $('.ui-no-lastchangeuser'),
+ permission: {
+ permission: $('.ui-permission'),
+ label: $('.ui-permission-label'),
+ freely: $('.ui-permission-freely'),
+ editable: $('.ui-permission-editable'),
+ locked: $('.ui-permission-locked'),
+ private: $('.ui-permission-private'),
+ limited: $('.ui-permission-limited'),
+ protected: $('.ui-permission-protected')
+ },
+ delete: $('.ui-delete-note')
+ },
+ toc: {
+ toc: $('.ui-toc'),
+ affix: $('.ui-affix-toc'),
+ label: $('.ui-toc-label'),
+ dropdown: $('.ui-toc-dropdown')
+ },
+ area: {
+ edit: $('.ui-edit-area'),
+ view: $('.ui-view-area'),
+ codemirror: $('.ui-edit-area .CodeMirror'),
+ codemirrorScroll: $('.ui-edit-area .CodeMirror .CodeMirror-scroll'),
+ codemirrorSizer: $('.ui-edit-area .CodeMirror .CodeMirror-sizer'),
+ codemirrorSizerInner: $('.ui-edit-area .CodeMirror .CodeMirror-sizer > div'),
+ markdown: $('.ui-view-area .markdown-body'),
+ resize: {
+ handle: $('.ui-resizable-handle'),
+ syncToggle: $('.ui-sync-toggle')
+ }
+ },
+ modal: {
+ snippetImportProjects: $('#snippetImportModalProjects'),
+ snippetImportSnippets: $('#snippetImportModalSnippets'),
+ revision: $('#revisionModal')
+ }
+}
+
+// page actions
var opts = {
- lines: 11, // The number of lines to draw
- length: 20, // The length of each line
- width: 2, // The line thickness
- radius: 30, // The radius of the inner circle
- corners: 0, // Corner roundness (0..1)
- rotate: 0, // The rotation offset
- direction: 1, // 1: clockwise, -1: counterclockwise
- color: '#000', // #rgb or #rrggbb or array of colors
- speed: 1.1, // Rounds per second
- trail: 60, // Afterglow percentage
- shadow: false, // Whether to render a shadow
- hwaccel: true, // Whether to use hardware acceleration
- className: 'spinner', // The CSS class to assign to the spinner
- zIndex: 2e9, // The z-index (defaults to 2000000000)
- top: '50%', // Top position relative to parent
- left: '50%' // Left position relative to parent
-};
-var spinner = new Spinner(opts).spin(ui.spinner[0]);
-
-//idle
+ lines: 11, // The number of lines to draw
+ length: 20, // The length of each line
+ width: 2, // The line thickness
+ radius: 30, // The radius of the inner circle
+ corners: 0, // Corner roundness (0..1)
+ rotate: 0, // The rotation offset
+ direction: 1, // 1: clockwise, -1: counterclockwise
+ color: '#000', // #rgb or #rrggbb or array of colors
+ speed: 1.1, // Rounds per second
+ trail: 60, // Afterglow percentage
+ shadow: false, // Whether to render a shadow
+ hwaccel: true, // Whether to use hardware acceleration
+ className: 'spinner', // The CSS class to assign to the spinner
+ zIndex: 2e9, // The z-index (defaults to 2000000000)
+ top: '50%', // Top position relative to parent
+ left: '50%' // Left position relative to parent
+}
+
+/* eslint-disable no-unused-vars */
+var spinner = new Spinner(opts).spin(ui.spinner[0])
+/* eslint-enable no-unused-vars */
+
+// idle
var idle = new Idle({
- onAway: function () {
- idle.isAway = true;
- emitUserStatus();
- updateOnlineStatus();
- },
- onAwayBack: function () {
- idle.isAway = false;
- emitUserStatus();
- updateOnlineStatus();
- setHaveUnreadChanges(false);
- updateTitleReminder();
- },
- awayTimeout: idleTime
-});
+ onAway: function () {
+ idle.isAway = true
+ emitUserStatus()
+ updateOnlineStatus()
+ },
+ onAwayBack: function () {
+ idle.isAway = false
+ emitUserStatus()
+ updateOnlineStatus()
+ setHaveUnreadChanges(false)
+ updateTitleReminder()
+ },
+ awayTimeout: idleTime
+})
ui.area.codemirror.on('touchstart', function () {
- idle.onActive();
-});
+ idle.onActive()
+})
-var haveUnreadChanges = false;
+var haveUnreadChanges = false
-function setHaveUnreadChanges(bool) {
- if (!loaded) return;
- if (bool && (idle.isAway || Visibility.hidden())) {
- haveUnreadChanges = true;
- } else if (!bool && !idle.isAway && !Visibility.hidden()) {
- haveUnreadChanges = false;
- }
+function setHaveUnreadChanges (bool) {
+ if (!window.loaded) return
+ if (bool && (idle.isAway || Visibility.hidden())) {
+ haveUnreadChanges = true
+ } else if (!bool && !idle.isAway && !Visibility.hidden()) {
+ haveUnreadChanges = false
+ }
}
-function updateTitleReminder() {
- if (!loaded) return;
- if (haveUnreadChanges) {
- document.title = '• ' + renderTitle(ui.area.markdown);
- } else {
- document.title = renderTitle(ui.area.markdown);
- }
+function updateTitleReminder () {
+ if (!window.loaded) return
+ if (haveUnreadChanges) {
+ document.title = '• ' + renderTitle(ui.area.markdown)
+ } else {
+ document.title = renderTitle(ui.area.markdown)
+ }
}
-function setRefreshModal(status) {
- $('#refreshModal').modal('show');
- $('#refreshModal').find('.modal-body > div').hide();
- $('#refreshModal').find('.' + status).show();
+function setRefreshModal (status) {
+ $('#refreshModal').modal('show')
+ $('#refreshModal').find('.modal-body > div').hide()
+ $('#refreshModal').find('.' + status).show()
}
-function setNeedRefresh() {
- needRefresh = true;
- editor.setOption('readOnly', true);
- socket.disconnect();
- showStatus(statusType.offline);
+function setNeedRefresh () {
+ window.needRefresh = true
+ editor.setOption('readOnly', true)
+ socket.disconnect()
+ showStatus(statusType.offline)
}
setloginStateChangeEvent(function () {
- setRefreshModal('user-state-changed');
- setNeedRefresh();
-});
+ setRefreshModal('user-state-changed')
+ setNeedRefresh()
+})
-//visibility
-var wasFocus = false;
+// visibility
+var wasFocus = false
Visibility.change(function (e, state) {
- var hidden = Visibility.hidden();
- if (hidden) {
- if (editorHasFocus()) {
- wasFocus = true;
- editor.getInputField().blur();
- }
- } else {
- if (wasFocus) {
- if (!visibleXS) {
- editor.focus();
- editor.refresh();
- }
- wasFocus = false;
- }
- setHaveUnreadChanges(false);
- }
- updateTitleReminder();
-});
+ var hidden = Visibility.hidden()
+ if (hidden) {
+ if (editorHasFocus()) {
+ wasFocus = true
+ editor.getInputField().blur()
+ }
+ } else {
+ if (wasFocus) {
+ if (!window.visibleXS) {
+ editor.focus()
+ editor.refresh()
+ }
+ wasFocus = false
+ }
+ setHaveUnreadChanges(false)
+ }
+ updateTitleReminder()
+})
-//when page ready
+// when page ready
$(document).ready(function () {
- idle.checkAway();
- checkResponsive();
- //if in smaller screen, we don't need advanced scrollbar
- var scrollbarStyle;
- if (visibleXS) {
- scrollbarStyle = 'native';
- } else {
- scrollbarStyle = 'overlay';
- }
- if (scrollbarStyle != editor.getOption('scrollbarStyle')) {
- editor.setOption('scrollbarStyle', scrollbarStyle);
- clearMap();
- }
- checkEditorStyle();
+ idle.checkAway()
+ checkResponsive()
+ // if in smaller screen, we don't need advanced scrollbar
+ var scrollbarStyle
+ if (window.visibleXS) {
+ scrollbarStyle = 'native'
+ } else {
+ scrollbarStyle = 'overlay'
+ }
+ if (scrollbarStyle !== editor.getOption('scrollbarStyle')) {
+ editor.setOption('scrollbarStyle', scrollbarStyle)
+ clearMap()
+ }
+ checkEditorStyle()
/* we need this only on touch devices */
- if (isTouchDevice) {
+ if (window.isTouchDevice) {
/* cache dom references */
- var $body = jQuery('body');
+ var $body = jQuery('body')
/* bind events */
- $(document)
+ $(document)
.on('focus', 'textarea, input', function () {
- $body.addClass('fixfixed');
+ $body.addClass('fixfixed')
})
.on('blur', 'textarea, input', function () {
- $body.removeClass('fixfixed');
- });
- }
- //showup
- $().showUp('.navbar', {
- upClass: 'navbar-hide',
- downClass: 'navbar-show'
- });
- //tooltip
- $('[data-toggle="tooltip"]').tooltip();
+ $body.removeClass('fixfixed')
+ })
+ }
+ // showup
+ $().showUp('.navbar', {
+ upClass: 'navbar-hide',
+ downClass: 'navbar-show'
+ })
+ // tooltip
+ $('[data-toggle="tooltip"]').tooltip()
// shortcuts
// allow on all tags
- key.filter = function (e) { return true; };
- key('ctrl+alt+e', function (e) {
- changeMode(modeType.edit);
- });
- key('ctrl+alt+v', function (e) {
- changeMode(modeType.view);
- });
- key('ctrl+alt+b', function (e) {
- changeMode(modeType.both);
- });
+ key.filter = function (e) { return true }
+ key('ctrl+alt+e', function (e) {
+ changeMode(modeType.edit)
+ })
+ key('ctrl+alt+v', function (e) {
+ changeMode(modeType.view)
+ })
+ key('ctrl+alt+b', function (e) {
+ changeMode(modeType.both)
+ })
// toggle-dropdown
- $(document).on('click', '.toggle-dropdown .dropdown-menu', function (e) {
- e.stopPropagation();
- });
-});
-//when page resize
+ $(document).on('click', '.toggle-dropdown .dropdown-menu', function (e) {
+ e.stopPropagation()
+ })
+})
+// when page resize
$(window).resize(function () {
- checkLayout();
- checkEditorStyle();
- checkTocStyle();
- checkCursorMenu();
- windowResize();
-});
-//when page unload
+ checkLayout()
+ checkEditorStyle()
+ checkTocStyle()
+ checkCursorMenu()
+ windowResize()
+})
+// when page unload
$(window).on('unload', function () {
- //updateHistoryInner();
-});
+ // updateHistoryInner();
+})
$(window).on('error', function () {
- //setNeedRefresh();
-});
-
-setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown);
+ // setNeedRefresh();
+})
-function autoSyncscroll() {
- if (editorHasFocus()) {
- syncScrollToView();
+setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown)
+
+function autoSyncscroll () {
+ if (editorHasFocus()) {
+ syncScrollToView()
+ } else {
+ syncScrollToEdit()
+ }
+}
+
+var windowResizeDebounce = 200
+var windowResize = _.debounce(windowResizeInner, windowResizeDebounce)
+
+function windowResizeInner (callback) {
+ checkLayout()
+ checkResponsive()
+ checkEditorStyle()
+ checkTocStyle()
+ checkCursorMenu()
+ // refresh editor
+ if (window.loaded) {
+ if (editor.getOption('scrollbarStyle') === 'native') {
+ setTimeout(function () {
+ clearMap()
+ autoSyncscroll()
+ updateScrollspy()
+ if (callback && typeof callback === 'function') { callback() }
+ }, 1)
} else {
- syncScrollToEdit();
- }
-}
-
-var windowResizeDebounce = 200;
-var windowResize = _.debounce(windowResizeInner, windowResizeDebounce);
-
-function windowResizeInner(callback) {
- checkLayout();
- checkResponsive();
- checkEditorStyle();
- checkTocStyle();
- checkCursorMenu();
- //refresh editor
- if (loaded) {
- if (editor.getOption('scrollbarStyle') === 'native') {
- setTimeout(function () {
- clearMap();
- autoSyncscroll();
- updateScrollspy();
- if (callback && typeof callback === 'function')
- callback();
- }, 1);
- } else {
// force it load all docs at once to prevent scroll knob blink
- editor.setOption('viewportMargin', Infinity);
- setTimeout(function () {
- clearMap();
- autoSyncscroll();
- editor.setOption('viewportMargin', viewportMargin);
- //add or update user cursors
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id != personalInfo.id)
- buildCursor(onlineUsers[i]);
- }
- updateScrollspy();
- if (callback && typeof callback === 'function')
- callback();
- }, 1);
+ editor.setOption('viewportMargin', Infinity)
+ setTimeout(function () {
+ clearMap()
+ autoSyncscroll()
+ editor.setOption('viewportMargin', viewportMargin)
+ // add or update user cursors
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id !== window.personalInfo.id) { buildCursor(window.onlineUsers[i]) }
}
+ updateScrollspy()
+ if (callback && typeof callback === 'function') { callback() }
+ }, 1)
}
+ }
}
-function checkLayout() {
- var navbarHieght = $('.navbar').outerHeight();
- $('body').css('padding-top', navbarHieght + 'px');
+function checkLayout () {
+ var navbarHieght = $('.navbar').outerHeight()
+ $('body').css('padding-top', navbarHieght + 'px')
}
-function editorHasFocus() {
- return $(editor.getInputField()).is(":focus");
+function editorHasFocus () {
+ return $(editor.getInputField()).is(':focus')
}
-//768-792px have a gap
-function checkResponsive() {
- visibleXS = $(".visible-xs").is(":visible");
- visibleSM = $(".visible-sm").is(":visible");
- visibleMD = $(".visible-md").is(":visible");
- visibleLG = $(".visible-lg").is(":visible");
+// 768-792px have a gap
+function checkResponsive () {
+ window.visibleXS = $('.visible-xs').is(':visible')
+ window.visibleSM = $('.visible-sm').is(':visible')
+ window.visibleMD = $('.visible-md').is(':visible')
+ window.visibleLG = $('.visible-lg').is(':visible')
- if (visibleXS && currentMode == modeType.both)
- if (editorHasFocus())
- changeMode(modeType.edit);
- else
- changeMode(modeType.view);
+ if (window.visibleXS && window.currentMode === modeType.both) {
+ if (editorHasFocus()) { changeMode(modeType.edit) } else { changeMode(modeType.view) }
+ }
- emitUserStatus();
+ emitUserStatus()
}
-var lastEditorWidth = 0;
-var previousFocusOnEditor = null;
+var lastEditorWidth = 0
+var previousFocusOnEditor = null
-function checkEditorStyle() {
- var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height();
+function checkEditorStyle () {
+ var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height()
// set editor height and min height based on scrollbar style and mode
- var scrollbarStyle = editor.getOption('scrollbarStyle');
- if (scrollbarStyle == 'overlay' || currentMode == modeType.both) {
- ui.area.codemirrorScroll.css('height', desireHeight + 'px');
- ui.area.codemirrorScroll.css('min-height', '');
- checkEditorScrollbar();
- } else if (scrollbarStyle == 'native') {
- ui.area.codemirrorScroll.css('height', '');
- ui.area.codemirrorScroll.css('min-height', desireHeight + 'px');
- }
+ var scrollbarStyle = editor.getOption('scrollbarStyle')
+ if (scrollbarStyle === 'overlay' || window.currentMode === modeType.both) {
+ ui.area.codemirrorScroll.css('height', desireHeight + 'px')
+ ui.area.codemirrorScroll.css('min-height', '')
+ checkEditorScrollbar()
+ } else if (scrollbarStyle === 'native') {
+ ui.area.codemirrorScroll.css('height', '')
+ ui.area.codemirrorScroll.css('min-height', desireHeight + 'px')
+ }
// workaround editor will have wrong doc height when editor height changed
- editor.setSize(null, ui.area.edit.height());
- //make editor resizable
- if (!ui.area.resize.handle.length) {
- ui.area.edit.resizable({
- handles: 'e',
- maxWidth: $(window).width() * 0.7,
- minWidth: $(window).width() * 0.2,
- create: function (e, ui) {
- $(this).parent().on('resize', function (e) {
- e.stopPropagation();
- });
- },
- start: function (e) {
- editor.setOption('viewportMargin', Infinity);
- },
- resize: function (e) {
- ui.area.resize.syncToggle.stop(true, true).show();
- checkTocStyle();
- },
- stop: function (e) {
- lastEditorWidth = ui.area.edit.width();
+ editor.setSize(null, ui.area.edit.height())
+ // make editor resizable
+ if (!ui.area.resize.handle.length) {
+ ui.area.edit.resizable({
+ handles: 'e',
+ maxWidth: $(window).width() * 0.7,
+ minWidth: $(window).width() * 0.2,
+ create: function (e, ui) {
+ $(this).parent().on('resize', function (e) {
+ e.stopPropagation()
+ })
+ },
+ start: function (e) {
+ editor.setOption('viewportMargin', Infinity)
+ },
+ resize: function (e) {
+ ui.area.resize.syncToggle.stop(true, true).show()
+ checkTocStyle()
+ },
+ stop: function (e) {
+ lastEditorWidth = ui.area.edit.width()
// workaround that scroll event bindings
- preventSyncScrollToView = 2;
- preventSyncScrollToEdit = true;
- editor.setOption('viewportMargin', viewportMargin);
- if (editorHasFocus()) {
- windowResizeInner(function () {
- ui.area.codemirrorScroll.scroll();
- });
- } else {
- windowResizeInner(function () {
- ui.area.view.scroll();
- });
- }
- checkEditorScrollbar();
- }
- });
- ui.area.resize.handle = $('.ui-resizable-handle');
- }
- if (!ui.area.resize.syncToggle.length) {
- ui.area.resize.syncToggle = $('<button class="btn btn-lg btn-default ui-sync-toggle" title="Toggle sync scrolling"><i class="fa fa-link fa-fw"></i></button>');
- ui.area.resize.syncToggle.hover(function () {
- previousFocusOnEditor = editorHasFocus();
- }, function () {
- previousFocusOnEditor = null;
- });
- ui.area.resize.syncToggle.click(function () {
- syncscroll = !syncscroll;
- checkSyncToggle();
- });
- ui.area.resize.handle.append(ui.area.resize.syncToggle);
- ui.area.resize.syncToggle.hide();
- ui.area.resize.handle.hover(function () {
- ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100);
- }, function () {
- ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300);
- });
- }
-}
-
-function checkSyncToggle() {
- if (syncscroll) {
- if (previousFocusOnEditor) {
- preventSyncScrollToView = false;
- syncScrollToView();
+ window.preventSyncScrollToView = 2
+ window.preventSyncScrollToEdit = true
+ editor.setOption('viewportMargin', viewportMargin)
+ if (editorHasFocus()) {
+ windowResizeInner(function () {
+ ui.area.codemirrorScroll.scroll()
+ })
} else {
- preventSyncScrollToEdit = false;
- syncScrollToEdit();
+ windowResizeInner(function () {
+ ui.area.view.scroll()
+ })
}
- ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link');
+ checkEditorScrollbar()
+ }
+ })
+ ui.area.resize.handle = $('.ui-resizable-handle')
+ }
+ if (!ui.area.resize.syncToggle.length) {
+ ui.area.resize.syncToggle = $('<button class="btn btn-lg btn-default ui-sync-toggle" title="Toggle sync scrolling"><i class="fa fa-link fa-fw"></i></button>')
+ ui.area.resize.syncToggle.hover(function () {
+ previousFocusOnEditor = editorHasFocus()
+ }, function () {
+ previousFocusOnEditor = null
+ })
+ ui.area.resize.syncToggle.click(function () {
+ window.syncscroll = !window.syncscroll
+ checkSyncToggle()
+ })
+ ui.area.resize.handle.append(ui.area.resize.syncToggle)
+ ui.area.resize.syncToggle.hide()
+ ui.area.resize.handle.hover(function () {
+ ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100)
+ }, function () {
+ ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300)
+ })
+ }
+}
+
+function checkSyncToggle () {
+ if (window.syncscroll) {
+ if (previousFocusOnEditor) {
+ window.preventSyncScrollToView = false
+ syncScrollToView()
} else {
- ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink');
+ window.preventSyncScrollToEdit = false
+ syncScrollToEdit()
}
+ ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link')
+ } else {
+ ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink')
+ }
}
var checkEditorScrollbar = _.debounce(function () {
- editor.operation(checkEditorScrollbarInner);
-}, 50);
+ editor.operation(checkEditorScrollbarInner)
+}, 50)
-function checkEditorScrollbarInner() {
+function checkEditorScrollbarInner () {
// workaround simple scroll bar knob
// will get wrong position when editor height changed
- var scrollInfo = editor.getScrollInfo();
- editor.scrollTo(null, scrollInfo.top - 1);
- editor.scrollTo(null, scrollInfo.top);
-}
-
-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');
- ui.toc.affix.css('width', rightMargin + '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) {
- ui.toc.toc.addClass('scrollspy-view');
- ui.toc.affix.addClass('scrollspy-view');
- } else 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;
- var status = ui.toolbar.status;
- var label = $('<span class="label"></span>');
- var fa = $('<i class="fa"></i>');
- var msg = "";
- var shortMsg = "";
-
- shortStatus.html("");
- status.html("");
-
- switch (currentStatus) {
- case statusType.connected:
- label.addClass(statusType.connected.label);
- fa.addClass(statusType.connected.fa);
- msg = statusType.connected.msg;
- break;
- case statusType.online:
- label.addClass(statusType.online.label);
- fa.addClass(statusType.online.fa);
- shortMsg = num;
- msg = num + " " + statusType.online.msg;
- break;
- case statusType.offline:
- label.addClass(statusType.offline.label);
- fa.addClass(statusType.offline.fa);
- msg = statusType.offline.msg;
- break;
- }
-
- label.append(fa);
- var shortLabel = label.clone();
-
- shortLabel.append(" " + shortMsg);
- shortStatus.append(shortLabel);
-
- label.append(" " + msg);
- status.append(label);
-}
-
-function toggleMode() {
- switch (currentMode) {
- case modeType.edit:
- changeMode(modeType.view);
- break;
- case modeType.view:
- changeMode(modeType.edit);
- break;
- case modeType.both:
- changeMode(modeType.view);
- break;
- }
-}
-
-var lastMode = null;
-
-function changeMode(type) {
+ var scrollInfo = editor.getScrollInfo()
+ editor.scrollTo(null, scrollInfo.top - 1)
+ editor.scrollTo(null, scrollInfo.top)
+}
+
+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')
+ ui.toc.affix.css('width', rightMargin + 'px')
+ } else {
+ newbool = false
+ }
+ // toc scrollspy
+ ui.toc.toc.removeClass('scrollspy-body, scrollspy-view')
+ ui.toc.affix.removeClass('scrollspy-body, scrollspy-view')
+ if (window.currentMode === modeType.both) {
+ ui.toc.toc.addClass('scrollspy-view')
+ ui.toc.affix.addClass('scrollspy-view')
+ } else if (window.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) {
+ window.currentStatus = type
+ var shortStatus = ui.toolbar.shortStatus
+ var status = ui.toolbar.status
+ var label = $('<span class="label"></span>')
+ var fa = $('<i class="fa"></i>')
+ var msg = ''
+ var shortMsg = ''
+
+ shortStatus.html('')
+ status.html('')
+
+ switch (window.currentStatus) {
+ case statusType.connected:
+ label.addClass(statusType.connected.label)
+ fa.addClass(statusType.connected.fa)
+ msg = statusType.connected.msg
+ break
+ case statusType.online:
+ label.addClass(statusType.online.label)
+ fa.addClass(statusType.online.fa)
+ shortMsg = num
+ msg = num + ' ' + statusType.online.msg
+ break
+ case statusType.offline:
+ label.addClass(statusType.offline.label)
+ fa.addClass(statusType.offline.fa)
+ msg = statusType.offline.msg
+ break
+ }
+
+ label.append(fa)
+ var shortLabel = label.clone()
+
+ shortLabel.append(' ' + shortMsg)
+ shortStatus.append(shortLabel)
+
+ label.append(' ' + msg)
+ status.append(label)
+}
+
+function toggleMode () {
+ switch (window.currentMode) {
+ case modeType.edit:
+ changeMode(modeType.view)
+ break
+ case modeType.view:
+ changeMode(modeType.edit)
+ break
+ case modeType.both:
+ changeMode(modeType.view)
+ break
+ }
+}
+
+var lastMode = null
+
+function changeMode (type) {
// lock navbar to prevent it hide after changeMode
- lockNavbar();
- saveInfo();
- if (type) {
- lastMode = currentMode;
- currentMode = type;
- }
- var responsiveClass = "col-lg-6 col-md-6 col-sm-6";
- var scrollClass = "ui-scrollable";
- ui.area.codemirror.removeClass(scrollClass);
- ui.area.edit.removeClass(responsiveClass);
- ui.area.view.removeClass(scrollClass);
- ui.area.view.removeClass(responsiveClass);
- switch (currentMode) {
- case modeType.edit:
- ui.area.edit.show();
- ui.area.view.hide();
- if (!editShown) {
- editor.refresh();
- editShown = true;
- }
- break;
- case modeType.view:
- ui.area.edit.hide();
- ui.area.view.show();
- break;
- case modeType.both:
- ui.area.codemirror.addClass(scrollClass);
- ui.area.edit.addClass(responsiveClass).show();
- ui.area.view.addClass(scrollClass);
- ui.area.view.show();
- break;
- }
+ lockNavbar()
+ saveInfo()
+ if (type) {
+ lastMode = window.currentMode
+ window.currentMode = type
+ }
+ var responsiveClass = 'col-lg-6 col-md-6 col-sm-6'
+ var scrollClass = 'ui-scrollable'
+ ui.area.codemirror.removeClass(scrollClass)
+ ui.area.edit.removeClass(responsiveClass)
+ ui.area.view.removeClass(scrollClass)
+ ui.area.view.removeClass(responsiveClass)
+ switch (window.currentMode) {
+ case modeType.edit:
+ ui.area.edit.show()
+ ui.area.view.hide()
+ if (!window.editShown) {
+ editor.refresh()
+ window.editShown = true
+ }
+ break
+ case modeType.view:
+ ui.area.edit.hide()
+ ui.area.view.show()
+ break
+ case modeType.both:
+ ui.area.codemirror.addClass(scrollClass)
+ ui.area.edit.addClass(responsiveClass).show()
+ ui.area.view.addClass(scrollClass)
+ ui.area.view.show()
+ break
+ }
// save mode to url
- if (history.replaceState && loaded) history.replaceState(null, "", serverurl + '/' + noteid + '?' + currentMode.name);
- if (currentMode == modeType.view) {
- editor.getInputField().blur();
- }
- if (currentMode == modeType.edit || currentMode == modeType.both) {
- ui.toolbar.uploadImage.fadeIn();
- //add and update status bar
- if (!statusBar) {
- addStatusBar();
- updateStatusBar();
- }
- //work around foldGutter might not init properly
- editor.setOption('foldGutter', false);
- editor.setOption('foldGutter', true);
- } 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'));
- }
- //check resizable editor style
- if (currentMode == modeType.both) {
- if (lastEditorWidth > 0)
- ui.area.edit.css('width', lastEditorWidth + 'px');
- else
- ui.area.edit.css('width', '');
- ui.area.resize.handle.show();
- } else {
- ui.area.edit.css('width', '');
- ui.area.resize.handle.hide();
- }
-
- windowResizeInner();
-
- restoreInfo();
-
- if (lastMode == modeType.view && currentMode == modeType.both) {
- preventSyncScrollToView = 2;
- syncScrollToEdit(null, true);
- }
-
- if (lastMode == modeType.edit && currentMode == modeType.both) {
- preventSyncScrollToEdit = 2;
- syncScrollToView(null, true);
- }
-
- if (lastMode == modeType.both && currentMode != modeType.both) {
- preventSyncScrollToView = false;
- preventSyncScrollToEdit = false;
- }
-
- if (lastMode != modeType.edit && currentMode == modeType.edit) {
- editor.refresh();
- }
-
- $(document.body).scrollspy('refresh');
- ui.area.view.scrollspy('refresh');
-
- ui.toolbar.both.removeClass("active");
- ui.toolbar.edit.removeClass("active");
- ui.toolbar.view.removeClass("active");
- var modeIcon = ui.toolbar.mode.find('i');
- modeIcon.removeClass('fa-pencil').removeClass('fa-eye');
- if (ui.area.edit.is(":visible") && ui.area.view.is(":visible")) { //both
- ui.toolbar.both.addClass("active");
- modeIcon.addClass('fa-eye');
- } else if (ui.area.edit.is(":visible")) { //edit
- ui.toolbar.edit.addClass("active");
- modeIcon.addClass('fa-eye');
- } else if (ui.area.view.is(":visible")) { //view
- ui.toolbar.view.addClass("active");
- modeIcon.addClass('fa-pencil');
- }
- unlockNavbar();
-}
-
-function lockNavbar() {
- $('.navbar').addClass('locked');
+ if (history.replaceState && window.loaded) history.replaceState(null, '', serverurl + '/' + noteid + '?' + window.currentMode.name)
+ if (window.currentMode === modeType.view) {
+ editor.getInputField().blur()
+ }
+ if (window.currentMode === modeType.edit || window.currentMode === modeType.both) {
+ ui.toolbar.uploadImage.fadeIn()
+ // add and update status bar
+ if (!statusBar) {
+ addStatusBar()
+ updateStatusBar()
+ }
+ // work around foldGutter might not init properly
+ editor.setOption('foldGutter', false)
+ editor.setOption('foldGutter', true)
+ } else {
+ ui.toolbar.uploadImage.fadeOut()
+ }
+ if (window.currentMode !== modeType.edit) {
+ $(document.body).css('background-color', 'white')
+ updateView()
+ } else {
+ $(document.body).css('background-color', ui.area.codemirror.css('background-color'))
+ }
+ // check resizable editor style
+ if (window.currentMode === modeType.both) {
+ if (lastEditorWidth > 0) { ui.area.edit.css('width', lastEditorWidth + 'px') } else { ui.area.edit.css('width', '') }
+ ui.area.resize.handle.show()
+ } else {
+ ui.area.edit.css('width', '')
+ ui.area.resize.handle.hide()
+ }
+
+ windowResizeInner()
+
+ restoreInfo()
+
+ if (lastMode === modeType.view && window.currentMode === modeType.both) {
+ window.preventSyncScrollToView = 2
+ syncScrollToEdit(null, true)
+ }
+
+ if (lastMode === modeType.edit && window.currentMode === modeType.both) {
+ window.preventSyncScrollToEdit = 2
+ syncScrollToView(null, true)
+ }
+
+ if (lastMode === modeType.both && window.currentMode !== modeType.both) {
+ window.preventSyncScrollToView = false
+ window.preventSyncScrollToEdit = false
+ }
+
+ if (lastMode !== modeType.edit && window.currentMode === modeType.edit) {
+ editor.refresh()
+ }
+
+ $(document.body).scrollspy('refresh')
+ ui.area.view.scrollspy('refresh')
+
+ ui.toolbar.both.removeClass('active')
+ ui.toolbar.edit.removeClass('active')
+ ui.toolbar.view.removeClass('active')
+ var modeIcon = ui.toolbar.mode.find('i')
+ modeIcon.removeClass('fa-pencil').removeClass('fa-eye')
+ if (ui.area.edit.is(':visible') && ui.area.view.is(':visible')) { // both
+ ui.toolbar.both.addClass('active')
+ modeIcon.addClass('fa-eye')
+ } else if (ui.area.edit.is(':visible')) { // edit
+ ui.toolbar.edit.addClass('active')
+ modeIcon.addClass('fa-eye')
+ } else if (ui.area.view.is(':visible')) { // view
+ ui.toolbar.view.addClass('active')
+ modeIcon.addClass('fa-pencil')
+ }
+ unlockNavbar()
+}
+
+function lockNavbar () {
+ $('.navbar').addClass('locked')
}
var unlockNavbar = _.debounce(function () {
- $('.navbar').removeClass('locked');
-}, 200);
-
-function closestIndex(arr, closestTo) {
- var closest = Math.max.apply(null, arr); //Get the highest number in arr in case it match nothing.
- var index = 0;
- for (var i = 0; i < arr.length; i++) { //Loop the array
- if (arr[i] >= closestTo && arr[i] < closest) {
- closest = arr[i]; //Check if it's higher than your number, but lower than your closest value
- index = i;
- }
- }
- return index; // return the value
-}
+ $('.navbar').removeClass('locked')
+}, 200)
-function showMessageModal(title, header, href, text, success) {
- var modal = $('.message-modal');
- modal.find('.modal-title').html(title);
- modal.find('.modal-body h5').html(header);
- if (href)
- modal.find('.modal-body a').attr('href', href).text(text);
- else
- modal.find('.modal-body a').removeAttr('href').text(text);
- modal.find('.modal-footer button').removeClass('btn-default btn-success btn-danger')
- if (success)
- modal.find('.modal-footer button').addClass('btn-success');
- else
- modal.find('.modal-footer button').addClass('btn-danger');
- modal.modal('show');
+function showMessageModal (title, header, href, text, success) {
+ var modal = $('.message-modal')
+ modal.find('.modal-title').html(title)
+ modal.find('.modal-body h5').html(header)
+ if (href) { modal.find('.modal-body a').attr('href', href).text(text) } else { modal.find('.modal-body a').removeAttr('href').text(text) }
+ modal.find('.modal-footer button').removeClass('btn-default btn-success btn-danger')
+ if (success) { modal.find('.modal-footer button').addClass('btn-success') } else { modal.find('.modal-footer button').addClass('btn-danger') }
+ modal.modal('show')
}
// check if dropbox app key is set and load scripts
if (DROPBOX_APP_KEY) {
- $('<script>')
+ $('<script>')
.attr('type', 'text/javascript')
.attr('src', 'https://www.dropbox.com/static/api/2/dropins.js')
.attr('id', 'dropboxjs')
.attr('data-app-key', DROPBOX_APP_KEY)
.prop('async', true)
.prop('defer', true)
- .appendTo('body');
+ .appendTo('body')
} else {
- ui.toolbar.import.dropbox.hide();
- ui.toolbar.export.dropbox.hide();
+ ui.toolbar.import.dropbox.hide()
+ ui.toolbar.export.dropbox.hide()
}
// check if google api key and client id are set and load scripts
if (GOOGLE_API_KEY && GOOGLE_CLIENT_ID) {
- $('<script>')
+ $('<script>')
.attr('type', 'text/javascript')
.attr('src', 'https://www.google.com/jsapi?callback=onGoogleAPILoaded')
.prop('async', true)
.prop('defer', true)
- .appendTo('body');
+ .appendTo('body')
} else {
- ui.toolbar.import.googleDrive.hide();
- ui.toolbar.export.googleDrive.hide();
+ ui.toolbar.import.googleDrive.hide()
+ ui.toolbar.export.googleDrive.hide()
}
-function onGoogleAPILoaded() {
- $('<script>')
+function onGoogleAPILoaded () {
+ $('<script>')
.attr('type', 'text/javascript')
.attr('src', 'https://apis.google.com/js/client:plusone.js?onload=onGoogleClientLoaded')
.prop('async', true)
.prop('defer', true)
- .appendTo('body');
+ .appendTo('body')
}
-window.onGoogleAPILoaded = onGoogleAPILoaded;
+window.onGoogleAPILoaded = onGoogleAPILoaded
-//button actions
-//share
-ui.toolbar.publish.attr("href", noteurl + "/publish");
+// button actions
+// share
+ui.toolbar.publish.attr('href', noteurl + '/publish')
// extra
-//slide
-ui.toolbar.extra.slide.attr("href", noteurl + "/slide");
-//download
-//markdown
+// slide
+ui.toolbar.extra.slide.attr('href', noteurl + '/slide')
+// download
+// markdown
ui.toolbar.download.markdown.click(function (e) {
- e.preventDefault();
- e.stopPropagation();
- var filename = renderFilename(ui.area.markdown) + '.md';
- var markdown = editor.getValue();
- var blob = new Blob([markdown], {
- type: "text/markdown;charset=utf-8"
- });
- saveAs(blob, filename, true);
-});
-//html
+ e.preventDefault()
+ e.stopPropagation()
+ var filename = renderFilename(ui.area.markdown) + '.md'
+ var markdown = editor.getValue()
+ var blob = new Blob([markdown], {
+ type: 'text/markdown;charset=utf-8'
+ })
+ saveAs(blob, filename, true)
+})
+// html
ui.toolbar.download.html.click(function (e) {
- e.preventDefault();
- e.stopPropagation();
- exportToHTML(ui.area.markdown);
-});
+ e.preventDefault()
+ e.stopPropagation()
+ exportToHTML(ui.area.markdown)
+})
// raw html
ui.toolbar.download.rawhtml.click(function (e) {
- e.preventDefault();
- e.stopPropagation();
- exportToRawHTML(ui.area.markdown);
-});
-//pdf
-ui.toolbar.download.pdf.attr("download", "").attr("href", noteurl + "/pdf");
-//export to dropbox
+ e.preventDefault()
+ e.stopPropagation()
+ exportToRawHTML(ui.area.markdown)
+})
+// pdf
+ui.toolbar.download.pdf.attr('download', '').attr('href', noteurl + '/pdf')
+// export to dropbox
ui.toolbar.export.dropbox.click(function () {
- var filename = renderFilename(ui.area.markdown) + '.md';
- var options = {
- files: [
- {
- 'url': noteurl + "/download",
- 'filename': filename
- }
- ],
- error: function (errorMessage) {
- console.error(errorMessage);
- }
- };
- Dropbox.save(options);
-});
-function uploadToGoogleDrive(accessToken) {
- ui.spinner.show();
- var filename = renderFilename(ui.area.markdown) + '.md';
- var markdown = editor.getValue();
- var blob = new Blob([markdown], {
- type: "text/markdown;charset=utf-8"
- });
- blob.name = filename;
- var uploader = new MediaUploader({
- file: blob,
- token: accessToken,
- onComplete: function (data) {
- data = JSON.parse(data);
- showMessageModal('<i class="fa fa-cloud-upload"></i> Export to Google Drive', 'Export Complete!', data.alternateLink, 'Click here to view your file', true);
- ui.spinner.hide();
- },
- onError: function (data) {
- var modal = $('.export-modal');
- showMessageModal('<i class="fa fa-cloud-upload"></i> Export to Google Drive', 'Export Error :(', '', data, false);
- ui.spinner.hide();
- }
- });
- uploader.upload();
-}
-function googleApiAuth(immediate, callback) {
- gapi.auth.authorize(
- {
- 'client_id': GOOGLE_CLIENT_ID,
- 'scope': 'https://www.googleapis.com/auth/drive.file',
- 'immediate': immediate
- }, callback ? callback : function () { });
-}
-function onGoogleClientLoaded() {
- googleApiAuth(true);
- buildImportFromGoogleDrive();
-}
-window.onGoogleClientLoaded = onGoogleClientLoaded;
+ var filename = renderFilename(ui.area.markdown) + '.md'
+ var options = {
+ files: [
+ {
+ 'url': noteurl + '/download',
+ 'filename': filename
+ }
+ ],
+ error: function (errorMessage) {
+ console.error(errorMessage)
+ }
+ }
+ Dropbox.save(options)
+})
+function uploadToGoogleDrive (accessToken) {
+ ui.spinner.show()
+ var filename = renderFilename(ui.area.markdown) + '.md'
+ var markdown = editor.getValue()
+ var blob = new Blob([markdown], {
+ type: 'text/markdown;charset=utf-8'
+ })
+ blob.name = filename
+ var uploader = new MediaUploader({
+ file: blob,
+ token: accessToken,
+ onComplete: function (data) {
+ data = JSON.parse(data)
+ showMessageModal('<i class="fa fa-cloud-upload"></i> Export to Google Drive', 'Export Complete!', data.alternateLink, 'Click here to view your file', true)
+ ui.spinner.hide()
+ },
+ onError: function (data) {
+ showMessageModal('<i class="fa fa-cloud-upload"></i> Export to Google Drive', 'Export Error :(', '', data, false)
+ ui.spinner.hide()
+ }
+ })
+ uploader.upload()
+}
+function googleApiAuth (immediate, callback) {
+ gapi.auth.authorize(
+ {
+ 'client_id': GOOGLE_CLIENT_ID,
+ 'scope': 'https://www.googleapis.com/auth/drive.file',
+ 'immediate': immediate
+ }, callback || function () { })
+}
+function onGoogleClientLoaded () {
+ googleApiAuth(true)
+ buildImportFromGoogleDrive()
+}
+window.onGoogleClientLoaded = onGoogleClientLoaded
// export to google drive
ui.toolbar.export.googleDrive.click(function (e) {
- var token = gapi.auth.getToken();
- if (token) {
- uploadToGoogleDrive(token.access_token);
- } else {
- googleApiAuth(false, function (result) {
- uploadToGoogleDrive(result.access_token);
- });
- }
-});
-//export to gist
-ui.toolbar.export.gist.attr("href", noteurl + "/gist");
-//export to snippet
-ui.toolbar.export.snippet.click(function() {
- ui.spinner.show();
- $.get(serverurl + '/auth/gitlab/callback/' + noteid + '/projects')
+ var token = gapi.auth.getToken()
+ if (token) {
+ uploadToGoogleDrive(token.access_token)
+ } else {
+ googleApiAuth(false, function (result) {
+ uploadToGoogleDrive(result.access_token)
+ })
+ }
+})
+// export to gist
+ui.toolbar.export.gist.attr('href', noteurl + '/gist')
+// export to snippet
+ui.toolbar.export.snippet.click(function () {
+ ui.spinner.show()
+ $.get(serverurl + '/auth/gitlab/callback/' + noteid + '/projects')
.done(function (data) {
- $("#snippetExportModalAccessToken").val(data.accesstoken);
- $("#snippetExportModalBaseURL").val(data.baseURL);
- $("#snippetExportModalLoading").hide();
- $("#snippetExportModal").modal('toggle');
- $("#snippetExportModalProjects").find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>');
- if (data.projects) {
- data.projects.sort(function(a,b) {
- return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0);
- });
- data.projects.forEach(function(project) {
- if (!project.snippets_enabled
- || (project.permissions.project_access === null && project.permissions.group_access === null)
- || (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20))
- {
- return;
- }
- $('<option>').val(project.id).text(project.path_with_namespace).appendTo("#snippetExportModalProjects");
- });
- $("#snippetExportModalProjects").prop('disabled', false);
- }
- $("#snippetExportModalLoading").hide();
+ $('#snippetExportModalAccessToken').val(data.accesstoken)
+ $('#snippetExportModalBaseURL').val(data.baseURL)
+ $('#snippetExportModalLoading').hide()
+ $('#snippetExportModal').modal('toggle')
+ $('#snippetExportModalProjects').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>')
+ if (data.projects) {
+ data.projects.sort(function (a, b) {
+ return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0)
+ })
+ data.projects.forEach(function (project) {
+ if (!project.snippets_enabled ||
+ (project.permissions.project_access === null && project.permissions.group_access === null) ||
+ (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20)) {
+ return
+ }
+ $('<option>').val(project.id).text(project.path_with_namespace).appendTo('#snippetExportModalProjects')
+ })
+ $('#snippetExportModalProjects').prop('disabled', false)
+ }
+ $('#snippetExportModalLoading').hide()
})
.fail(function (data) {
- showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false);
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false)
})
.always(function () {
- ui.spinner.hide();
- });
-});
-//import from dropbox
+ ui.spinner.hide()
+ })
+})
+// import from dropbox
ui.toolbar.import.dropbox.click(function () {
- var options = {
- success: function (files) {
- ui.spinner.show();
- var url = files[0].link;
- importFromUrl(url);
- },
- linkType: "direct",
- multiselect: false,
- extensions: ['.md', '.html']
- };
- Dropbox.choose(options);
-});
+ var options = {
+ success: function (files) {
+ ui.spinner.show()
+ var url = files[0].link
+ importFromUrl(url)
+ },
+ linkType: 'direct',
+ multiselect: false,
+ extensions: ['.md', '.html']
+ }
+ Dropbox.choose(options)
+})
// import from google drive
-var picker = null;
-function buildImportFromGoogleDrive() {
- picker = new FilePicker({
- apiKey: GOOGLE_API_KEY,
- clientId: GOOGLE_CLIENT_ID,
- buttonEl: ui.toolbar.import.googleDrive,
- onSelect: function (file) {
- if (file.downloadUrl) {
- ui.spinner.show();
- var accessToken = gapi.auth.getToken().access_token;
- $.ajax({
- type: 'GET',
- beforeSend: function (request) {
- request.setRequestHeader('Authorization', 'Bearer ' + accessToken);
- },
- url: file.downloadUrl,
- success: function (data) {
- if (file.fileExtension == 'html')
- parseToEditor(data);
- else
- replaceAll(data);
- },
- error: function (data) {
- showMessageModal('<i class="fa fa-cloud-download"></i> Import from Google Drive', 'Import failed :(', '', data, false);
- },
- complete: function () {
- ui.spinner.hide();
- }
- });
- }
- }
- });
+function buildImportFromGoogleDrive () {
+ FilePicker({
+ apiKey: GOOGLE_API_KEY,
+ clientId: GOOGLE_CLIENT_ID,
+ buttonEl: ui.toolbar.import.googleDrive,
+ onSelect: function (file) {
+ if (file.downloadUrl) {
+ ui.spinner.show()
+ var accessToken = gapi.auth.getToken().access_token
+ $.ajax({
+ type: 'GET',
+ beforeSend: function (request) {
+ request.setRequestHeader('Authorization', 'Bearer ' + accessToken)
+ },
+ url: file.downloadUrl,
+ success: function (data) {
+ if (file.fileExtension === 'html') { parseToEditor(data) } else { replaceAll(data) }
+ },
+ error: function (data) {
+ showMessageModal('<i class="fa fa-cloud-download"></i> Import from Google Drive', 'Import failed :(', '', data, false)
+ },
+ complete: function () {
+ ui.spinner.hide()
+ }
+ })
+ }
+ }
+ })
}
-//import from gist
+// import from gist
ui.toolbar.import.gist.click(function () {
- //na
-});
-//import from snippet
+ // na
+})
+// import from snippet
ui.toolbar.import.snippet.click(function () {
- ui.spinner.show();
- $.get(serverurl + '/auth/gitlab/callback/' + noteid + '/projects')
+ ui.spinner.show()
+ $.get(serverurl + '/auth/gitlab/callback/' + noteid + '/projects')
.done(function (data) {
- $("#snippetImportModalAccessToken").val(data.accesstoken);
- $("#snippetImportModalBaseURL").val(data.baseURL);
- $("#snippetImportModalContent").prop('disabled', false);
- $("#snippetImportModalConfirm").prop('disabled', false);
- $("#snippetImportModalLoading").hide();
- $("#snippetImportModal").modal('toggle');
- $("#snippetImportModalProjects").find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>');
- if (data.projects) {
- data.projects.sort(function(a,b) {
- return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0);
- });
- data.projects.forEach(function(project) {
- if (!project.snippets_enabled
- || (project.permissions.project_access === null && project.permissions.group_access === null)
- || (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20))
- {
- return;
- }
- $('<option>').val(project.id).text(project.path_with_namespace).appendTo("#snippetImportModalProjects");
- });
- $("#snippetImportModalProjects").prop('disabled', false);
- }
- $("#snippetImportModalLoading").hide();
+ $('#snippetImportModalAccessToken').val(data.accesstoken)
+ $('#snippetImportModalBaseURL').val(data.baseURL)
+ $('#snippetImportModalContent').prop('disabled', false)
+ $('#snippetImportModalConfirm').prop('disabled', false)
+ $('#snippetImportModalLoading').hide()
+ $('#snippetImportModal').modal('toggle')
+ $('#snippetImportModalProjects').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>')
+ if (data.projects) {
+ data.projects.sort(function (a, b) {
+ return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0)
+ })
+ data.projects.forEach(function (project) {
+ if (!project.snippets_enabled ||
+ (project.permissions.project_access === null && project.permissions.group_access === null) ||
+ (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20)) {
+ return
+ }
+ $('<option>').val(project.id).text(project.path_with_namespace).appendTo('#snippetImportModalProjects')
+ })
+ $('#snippetImportModalProjects').prop('disabled', false)
+ }
+ $('#snippetImportModalLoading').hide()
})
.fail(function (data) {
- showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false);
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false)
})
.always(function () {
- ui.spinner.hide();
- });
-});
-//import from clipboard
+ ui.spinner.hide()
+ })
+})
+// import from clipboard
ui.toolbar.import.clipboard.click(function () {
- //na
-});
-//upload image
+ // 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
+ 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();
-});
+ e.stopPropagation()
+})
// prevent empty link change hash
$('a[href="#"]').click(function (e) {
- e.preventDefault();
-});
-
-//modal actions
-var revisions = [];
-var revisionViewer = null;
-var revisionInsert = [];
-var revisionDelete = [];
-var revisionInsertAnnotation = null;
-var revisionDeleteAnnotation = null;
-var revisionList = ui.modal.revision.find('.ui-revision-list');
-var revision = null;
-var revisionTime = null;
+ e.preventDefault()
+})
+
+// modal actions
+var revisions = []
+var revisionViewer = null
+var revisionInsert = []
+var revisionDelete = []
+var revisionInsertAnnotation = null
+var revisionDeleteAnnotation = null
+var revisionList = ui.modal.revision.find('.ui-revision-list')
+var revision = null
+var revisionTime = null
ui.modal.revision.on('show.bs.modal', function (e) {
- $.get(noteurl + '/revision')
- .done(function(data) {
- parseRevisions(data.revision);
- initRevisionViewer();
+ $.get(noteurl + '/revision')
+ .done(function (data) {
+ parseRevisions(data.revision)
+ initRevisionViewer()
})
- .fail(function(err) {
-
+ .fail(function (err) {
+ if (debug) {
+ console.log(err)
+ }
})
- .always(function() {
- //na
- });
-});
-function checkRevisionViewer() {
- if (revisionViewer) {
- var container = $(revisionViewer.display.wrapper).parent();
- $(revisionViewer.display.scroller).css('height', container.height() + 'px');
- revisionViewer.refresh();
- }
-}
-ui.modal.revision.on('shown.bs.modal', checkRevisionViewer);
-$(window).resize(checkRevisionViewer);
-function parseRevisions(_revisions) {
- if (_revisions.length != revisions) {
- revisions = _revisions;
- var lastRevision = null;
- if (revisionList.children().length > 0) {
- lastRevision = revisionList.find('.active').attr('data-revision-time');
- }
- revisionList.html('');
- for (var i = 0; i < revisions.length; i++) {
- var revision = revisions[i];
- var item = $('<a href="#" class="list-group-item"></a>');
- item.attr('data-revision-time', revision.time);
- if (lastRevision == revision.time) item.addClass('active');
- var itemHeading = $('<h5 class="list-group-item-heading"></h5>');
- itemHeading.html('<i class="fa fa-clock-o"></i> ' + moment(revision.time).format('llll'));
- var itemText = $('<p class="list-group-item-text"></p>');
- itemText.html('<i class="fa fa-file-text"></i> Length: ' + revision.length);
- item.append(itemHeading).append(itemText);
- item.click(function (e) {
- var time = $(this).attr('data-revision-time');
- selectRevision(time);
- });
- revisionList.append(item);
- }
- if (!lastRevision) {
- selectRevision(revisions[0].time);
- }
- }
-}
-function selectRevision(time) {
- if (time == revisionTime) return;
- $.get(noteurl + '/revision/' + time)
- .done(function(data) {
- revision = data;
- revisionTime = time;
- var lastScrollInfo = revisionViewer.getScrollInfo();
- revisionList.children().removeClass('active');
- revisionList.find('[data-revision-time="' + time + '"]').addClass('active');
- var content = revision.content;
- revisionViewer.setValue(content);
- revisionViewer.scrollTo(null, lastScrollInfo.top);
- revisionInsert = [];
- revisionDelete = [];
+ .always(function () {
+ // na
+ })
+})
+function checkRevisionViewer () {
+ if (revisionViewer) {
+ var container = $(revisionViewer.display.wrapper).parent()
+ $(revisionViewer.display.scroller).css('height', container.height() + 'px')
+ revisionViewer.refresh()
+ }
+}
+ui.modal.revision.on('shown.bs.modal', checkRevisionViewer)
+$(window).resize(checkRevisionViewer)
+function parseRevisions (_revisions) {
+ if (_revisions.length !== revisions) {
+ revisions = _revisions
+ var lastRevision = null
+ if (revisionList.children().length > 0) {
+ lastRevision = revisionList.find('.active').attr('data-revision-time')
+ }
+ revisionList.html('')
+ for (var i = 0; i < revisions.length; i++) {
+ var revision = revisions[i]
+ var item = $('<a href="#" class="list-group-item"></a>')
+ item.attr('data-revision-time', revision.time)
+ if (lastRevision === revision.time) item.addClass('active')
+ var itemHeading = $('<h5 class="list-group-item-heading"></h5>')
+ itemHeading.html('<i class="fa fa-clock-o"></i> ' + moment(revision.time).format('llll'))
+ var itemText = $('<p class="list-group-item-text"></p>')
+ itemText.html('<i class="fa fa-file-text"></i> Length: ' + revision.length)
+ item.append(itemHeading).append(itemText)
+ item.click(function (e) {
+ var time = $(this).attr('data-revision-time')
+ selectRevision(time)
+ })
+ revisionList.append(item)
+ }
+ if (!lastRevision) {
+ selectRevision(revisions[0].time)
+ }
+ }
+}
+function selectRevision (time) {
+ if (time === revisionTime) return
+ $.get(noteurl + '/revision/' + time)
+ .done(function (data) {
+ revision = data
+ revisionTime = time
+ var lastScrollInfo = revisionViewer.getScrollInfo()
+ revisionList.children().removeClass('active')
+ revisionList.find('[data-revision-time="' + time + '"]').addClass('active')
+ var content = revision.content
+ revisionViewer.setValue(content)
+ revisionViewer.scrollTo(null, lastScrollInfo.top)
+ revisionInsert = []
+ revisionDelete = []
// mark the text which have been insert or delete
- if (revision.patch.length > 0) {
- var bias = 0;
- for (var j = 0; j < revision.patch.length; j++) {
- var patch = revision.patch[j];
- var currIndex = patch.start1 + bias;
- for (var i = 0; i < patch.diffs.length; i++) {
- var diff = patch.diffs[i];
+ if (revision.patch.length > 0) {
+ var bias = 0
+ for (var j = 0; j < revision.patch.length; j++) {
+ var patch = revision.patch[j]
+ var currIndex = patch.start1 + bias
+ for (var i = 0; i < patch.diffs.length; i++) {
+ var diff = patch.diffs[i]
// ignore if diff only contains line breaks
- if ((diff[1].match(new RegExp("\n", "g")) || []).length == diff[1].length) continue;
- switch(diff[0]) {
- case 0: // retain
- currIndex += diff[1].length;
- break;
- case 1: // insert
- var prePos = revisionViewer.posFromIndex(currIndex);
- var postPos = revisionViewer.posFromIndex(currIndex + diff[1].length);
- revisionInsert.push({
- from: prePos,
- to: postPos
- });
- revisionViewer.markText(prePos, postPos, {
- css: 'background-color: rgba(230,255,230,0.7); text-decoration: underline;'
- });
- currIndex += diff[1].length;
- break;
- case -1: // delete
- var prePos = revisionViewer.posFromIndex(currIndex);
- revisionViewer.replaceRange(diff[1], prePos);
- var postPos = revisionViewer.posFromIndex(currIndex + diff[1].length);
- revisionDelete.push({
- from: prePos,
- to: postPos
- });
- revisionViewer.markText(prePos, postPos, {
- css: 'background-color: rgba(255,230,230,0.7); text-decoration: line-through;'
- });
- bias += diff[1].length;
- currIndex += diff[1].length;
- break;
- }
- }
+ if ((diff[1].match(/\n/g) || []).length === diff[1].length) continue
+ var prePos
+ var postPos
+ switch (diff[0]) {
+ case 0: // retain
+ currIndex += diff[1].length
+ break
+ case 1: // insert
+ prePos = revisionViewer.posFromIndex(currIndex)
+ postPos = revisionViewer.posFromIndex(currIndex + diff[1].length)
+ revisionInsert.push({
+ from: prePos,
+ to: postPos
+ })
+ revisionViewer.markText(prePos, postPos, {
+ css: 'background-color: rgba(230,255,230,0.7); text-decoration: underline;'
+ })
+ currIndex += diff[1].length
+ break
+ case -1: // delete
+ prePos = revisionViewer.posFromIndex(currIndex)
+ revisionViewer.replaceRange(diff[1], prePos)
+ postPos = revisionViewer.posFromIndex(currIndex + diff[1].length)
+ revisionDelete.push({
+ from: prePos,
+ to: postPos
+ })
+ revisionViewer.markText(prePos, postPos, {
+ css: 'background-color: rgba(255,230,230,0.7); text-decoration: line-through;'
+ })
+ bias += diff[1].length
+ currIndex += diff[1].length
+ break
}
+ }
}
- revisionInsertAnnotation.update(revisionInsert);
- revisionDeleteAnnotation.update(revisionDelete);
+ }
+ revisionInsertAnnotation.update(revisionInsert)
+ revisionDeleteAnnotation.update(revisionDelete)
})
- .fail(function(err) {
-
+ .fail(function (err) {
+ if (debug) {
+ console.log(err)
+ }
+ })
+ .always(function () {
+ // na
})
- .always(function() {
- //na
- });
-}
-function initRevisionViewer() {
- if (revisionViewer) return;
- var revisionViewerTextArea = document.getElementById("revisionViewer");
- revisionViewer = CodeMirror.fromTextArea(revisionViewerTextArea, {
- mode: defaultEditorMode,
- viewportMargin: viewportMargin,
- lineNumbers: true,
- lineWrapping: true,
- showCursorWhenSelecting: true,
- inputStyle: "textarea",
- gutters: ["CodeMirror-linenumbers"],
- flattenSpans: true,
- addModeClass: true,
- readOnly: true,
- autoRefresh: true,
- scrollbarStyle: 'overlay'
- });
- revisionInsertAnnotation = revisionViewer.annotateScrollbar({ className:"CodeMirror-insert-match" });
- revisionDeleteAnnotation = revisionViewer.annotateScrollbar({ className:"CodeMirror-delete-match" });
- checkRevisionViewer();
+}
+function initRevisionViewer () {
+ if (revisionViewer) return
+ var revisionViewerTextArea = document.getElementById('revisionViewer')
+ revisionViewer = CodeMirror.fromTextArea(revisionViewerTextArea, {
+ mode: defaultEditorMode,
+ viewportMargin: viewportMargin,
+ lineNumbers: true,
+ lineWrapping: true,
+ showCursorWhenSelecting: true,
+ inputStyle: 'textarea',
+ gutters: ['CodeMirror-linenumbers'],
+ flattenSpans: true,
+ addModeClass: true,
+ readOnly: true,
+ autoRefresh: true,
+ scrollbarStyle: 'overlay'
+ })
+ revisionInsertAnnotation = revisionViewer.annotateScrollbar({ className: 'CodeMirror-insert-match' })
+ revisionDeleteAnnotation = revisionViewer.annotateScrollbar({ className: 'CodeMirror-delete-match' })
+ checkRevisionViewer()
}
$('#revisionModalDownload').click(function () {
- if (!revision) return;
- var filename = renderFilename(ui.area.markdown) + '_' + revisionTime + '.md';
- var blob = new Blob([revision.content], {
- type: "text/markdown;charset=utf-8"
- });
- saveAs(blob, filename, true);
-});
+ if (!revision) return
+ var filename = renderFilename(ui.area.markdown) + '_' + revisionTime + '.md'
+ var blob = new Blob([revision.content], {
+ type: 'text/markdown;charset=utf-8'
+ })
+ saveAs(blob, filename, true)
+})
$('#revisionModalRevert').click(function () {
- if (!revision) return;
- editor.setValue(revision.content);
- ui.modal.revision.modal('hide');
-});
-//snippet projects
-ui.modal.snippetImportProjects.change(function() {
- var accesstoken = $("#snippetImportModalAccessToken").val(),
- baseURL = $("#snippetImportModalBaseURL").val(),
- project = $("#snippetImportModalProjects").val();
-
- $("#snippetImportModalLoading").show();
- $("#snippetImportModalContent").val('/projects/' + project);
- $.get(baseURL + '/api/v3/projects/' + project + '/snippets?access_token=' + accesstoken)
- .done(function(data) {
- $("#snippetImportModalSnippets").find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Snippets</option>');
- data.forEach(function(snippet) {
- $('<option>').val(snippet.id).text(snippet.title).appendTo($("#snippetImportModalSnippets"));
- });
- $("#snippetImportModalLoading").hide();
- $("#snippetImportModalSnippets").prop('disabled', false);
+ if (!revision) return
+ editor.setValue(revision.content)
+ ui.modal.revision.modal('hide')
+})
+// snippet projects
+ui.modal.snippetImportProjects.change(function () {
+ var accesstoken = $('#snippetImportModalAccessToken').val()
+ var baseURL = $('#snippetImportModalBaseURL').val()
+ var project = $('#snippetImportModalProjects').val()
+
+ $('#snippetImportModalLoading').show()
+ $('#snippetImportModalContent').val('/projects/' + project)
+ $.get(baseURL + '/api/v3/projects/' + project + '/snippets?access_token=' + accesstoken)
+ .done(function (data) {
+ $('#snippetImportModalSnippets').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Snippets</option>')
+ data.forEach(function (snippet) {
+ $('<option>').val(snippet.id).text(snippet.title).appendTo($('#snippetImportModalSnippets'))
+ })
+ $('#snippetImportModalLoading').hide()
+ $('#snippetImportModalSnippets').prop('disabled', false)
})
- .fail(function(err) {
-
+ .fail(function (err) {
+ if (debug) {
+ console.log(err)
+ }
})
- .always(function() {
- //na
- });
-});
-//snippet snippets
-ui.modal.snippetImportSnippets.change(function() {
- var project = $("#snippetImportModalProjects").val(),
- snippet = $("#snippetImportModalSnippets").val();
-
- $("#snippetImportModalContent").val($("#snippetImportModalContent").val() + '/snippets/' + snippet);
-})
-
-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 {
- $('body, html').stop(true, true).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 {
- $('body, html').stop(true, true).animate({
- scrollTop: $(document.body)[0].scrollHeight
- }, 100, "linear");
- }
-}
-
-window.scrollToTop = scrollToTop;
-window.scrollToBottom = scrollToBottom;
-
-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();
-}
+ .always(function () {
+ // na
+ })
+})
+// snippet snippets
+ui.modal.snippetImportSnippets.change(function () {
+ var snippet = $('#snippetImportModalSnippets').val()
+ $('#snippetImportModalContent').val($('#snippetImportModalContent').val() + '/snippets/' + snippet)
+})
-function updateScrollspy() {
- var headers = ui.area.markdown.find('h1, h2, h3').toArray();
- var headerMap = [];
- for (var i = 0; i < headers.length; i++) {
- headerMap.push($(headers[i]).offset().top - parseInt($(headers[i]).css('margin-top')));
- }
- applyScrollspyActive($(window).scrollTop(), headerMap, headers,
- $('.scrollspy-body'), 0);
- var offset = ui.area.view.scrollTop() - ui.area.view.offset().top;
- applyScrollspyActive(ui.area.view.scrollTop(), headerMap, headers,
- $('.scrollspy-view'), offset - 10);
-}
-
-function applyScrollspyActive(top, headerMap, headers, target, offset) {
- var index = 0;
- for (var i = headerMap.length - 1; i >= 0; i--) {
- if (top >= (headerMap[i] + offset) && headerMap[i + 1] && top < (headerMap[i + 1] + offset)) {
- index = i;
- break;
- }
- }
- var header = $(headers[index]);
- var active = target.find('a[href="#' + header.attr('id') + '"]');
- active.closest('li').addClass('active').parent().closest('li').addClass('active').parent().closest('li').addClass('active');
+function scrollToTop () {
+ if (window.currentMode === modeType.both) {
+ if (editor.getScrollInfo().top !== 0) { editor.scrollTo(0, 0) } else {
+ ui.area.view.animate({
+ scrollTop: 0
+ }, 100, 'linear')
+ }
+ } else {
+ $('body, html').stop(true, true).animate({
+ scrollTop: 0
+ }, 100, 'linear')
+ }
+}
+
+function scrollToBottom () {
+ if (window.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 {
+ $('body, html').stop(true, true).animate({
+ scrollTop: $(document.body)[0].scrollHeight
+ }, 100, 'linear')
+ }
+}
+
+window.scrollToTop = scrollToTop
+window.scrollToBottom = scrollToBottom
+
+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();
+}
+
+function updateScrollspy () {
+ var headers = ui.area.markdown.find('h1, h2, h3').toArray()
+ var headerMap = []
+ for (var i = 0; i < headers.length; i++) {
+ headerMap.push($(headers[i]).offset().top - parseInt($(headers[i]).css('margin-top')))
+ }
+ applyScrollspyActive($(window).scrollTop(), headerMap, headers,
+ $('.scrollspy-body'), 0)
+ var offset = ui.area.view.scrollTop() - ui.area.view.offset().top
+ applyScrollspyActive(ui.area.view.scrollTop(), headerMap, headers,
+ $('.scrollspy-view'), offset - 10)
+}
+
+function applyScrollspyActive (top, headerMap, headers, target, offset) {
+ var index = 0
+ for (var i = headerMap.length - 1; i >= 0; i--) {
+ if (top >= (headerMap[i] + offset) && headerMap[i + 1] && top < (headerMap[i + 1] + offset)) {
+ index = i
+ break
+ }
+ }
+ var header = $(headers[index])
+ var active = target.find('a[href="#' + header.attr('id') + '"]')
+ active.closest('li').addClass('active').parent().closest('li').addClass('active').parent().closest('li').addClass('active')
}
// clipboard modal
-//fix for wrong autofocus
+// fix for wrong autofocus
$('#clipboardModal').on('shown.bs.modal', function () {
- $('#clipboardModal').blur();
-});
-$("#clipboardModalClear").click(function () {
- $("#clipboardModalContent").html('');
-});
-$("#clipboardModalConfirm").click(function () {
- var data = $("#clipboardModalContent").html();
- if (data) {
- parseToEditor(data);
- $('#clipboardModal').modal('hide');
- $("#clipboardModalContent").html('');
- }
-});
+ $('#clipboardModal').blur()
+})
+$('#clipboardModalClear').click(function () {
+ $('#clipboardModalContent').html('')
+})
+$('#clipboardModalConfirm').click(function () {
+ var data = $('#clipboardModalContent').html()
+ if (data) {
+ parseToEditor(data)
+ $('#clipboardModal').modal('hide')
+ $('#clipboardModalContent').html('')
+ }
+})
// refresh modal
$('#refreshModalRefresh').click(function () {
- location.reload(true);
-});
+ location.reload(true)
+})
// gist import modal
-$("#gistImportModalClear").click(function () {
- $("#gistImportModalContent").val('');
-});
-$("#gistImportModalConfirm").click(function () {
- var gisturl = $("#gistImportModalContent").val();
- if (!gisturl) return;
- $('#gistImportModal').modal('hide');
- $("#gistImportModalContent").val('');
- if (!isValidURL(gisturl)) {
- showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid URL :(', '', '', false);
- return;
+$('#gistImportModalClear').click(function () {
+ $('#gistImportModalContent').val('')
+})
+$('#gistImportModalConfirm').click(function () {
+ var gisturl = $('#gistImportModalContent').val()
+ if (!gisturl) return
+ $('#gistImportModal').modal('hide')
+ $('#gistImportModalContent').val('')
+ if (!isValidURL(gisturl)) {
+ showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid URL :(', '', '', false)
+ } else {
+ var hostname = window.url('hostname', gisturl)
+ if (hostname !== 'gist.github.com') {
+ showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', '', false)
} else {
- var hostname = url('hostname', gisturl)
- if (hostname !== 'gist.github.com') {
- showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', '', false);
- } else {
- ui.spinner.show();
- $.get('https://api.github.com/gists/' + url('-1', gisturl))
+ ui.spinner.show()
+ $.get('https://api.github.com/gists/' + window.url('-1', gisturl))
.done(function (data) {
- if (data.files) {
- var contents = "";
- Object.keys(data.files).forEach(function (key) {
- contents += key;
- contents += '\n---\n';
- contents += data.files[key].content;
- contents += '\n\n';
- });
- replaceAll(contents);
- } else {
- showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Unable to fetch gist files :(', '', '', false);
- }
+ if (data.files) {
+ var contents = ''
+ Object.keys(data.files).forEach(function (key) {
+ contents += key
+ contents += '\n---\n'
+ contents += data.files[key].content
+ contents += '\n\n'
+ })
+ replaceAll(contents)
+ } else {
+ showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Unable to fetch gist files :(', '', '', false)
+ }
})
.fail(function (data) {
- showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', JSON.stringify(data), false);
+ showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', JSON.stringify(data), false)
})
.always(function () {
- ui.spinner.hide();
- });
- }
+ ui.spinner.hide()
+ })
}
-});
+ }
+})
// snippet import modal
-$("#snippetImportModalClear").click(function () {
- $("#snippetImportModalContent").val('');
- $("#snippetImportModalProjects").val('init');
- $("#snippetImportModalSnippets").val('init');
- $("#snippetImportModalSnippets").prop('disabled', true);
-});
-$("#snippetImportModalConfirm").click(function () {
- var snippeturl = $("#snippetImportModalContent").val();
- if (!snippeturl) return;
- $('#snippetImportModal').modal('hide');
- $("#snippetImportModalContent").val('');
- if (!/^.+\/snippets\/.+$/.test(snippeturl)) {
- showMessageModal('<i class="fa fa-github"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', '', false);
- } else {
- ui.spinner.show();
- var accessToken = '?access_token=' + $("#snippetImportModalAccessToken").val();
- var fullURL = $("#snippetImportModalBaseURL").val() + '/api/v3' + snippeturl;
- $.get(fullURL + accessToken)
- .done(function(data) {
- var content = '# ' + (data.title || "Snippet Import");
- var fileInfo = data.file_name.split('.');
- fileInfo[1] = (fileInfo[1]) ? fileInfo[1] : "md";
- $.get(fullURL + '/raw' + accessToken)
+$('#snippetImportModalClear').click(function () {
+ $('#snippetImportModalContent').val('')
+ $('#snippetImportModalProjects').val('init')
+ $('#snippetImportModalSnippets').val('init')
+ $('#snippetImportModalSnippets').prop('disabled', true)
+})
+$('#snippetImportModalConfirm').click(function () {
+ var snippeturl = $('#snippetImportModalContent').val()
+ if (!snippeturl) return
+ $('#snippetImportModal').modal('hide')
+ $('#snippetImportModalContent').val('')
+ if (!/^.+\/snippets\/.+$/.test(snippeturl)) {
+ showMessageModal('<i class="fa fa-github"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', '', false)
+ } else {
+ ui.spinner.show()
+ var accessToken = '?access_token=' + $('#snippetImportModalAccessToken').val()
+ var fullURL = $('#snippetImportModalBaseURL').val() + '/api/v3' + snippeturl
+ $.get(fullURL + accessToken)
+ .done(function (data) {
+ var content = '# ' + (data.title || 'Snippet Import')
+ var fileInfo = data.file_name.split('.')
+ fileInfo[1] = (fileInfo[1]) ? fileInfo[1] : 'md'
+ $.get(fullURL + '/raw' + accessToken)
.done(function (raw) {
- if (raw) {
- content += "\n\n";
- if (fileInfo[1] != "md") {
- content += "```" + fileTypes[fileInfo[1]] + "\n";
- }
- content += raw;
- if (fileInfo[1] != "md") {
- content += "\n```";
- }
- replaceAll(content);
+ if (raw) {
+ content += '\n\n'
+ if (fileInfo[1] !== 'md') {
+ content += '```' + window.fileTypes[fileInfo[1]] + '\n'
+ }
+ content += raw
+ if (fileInfo[1] !== 'md') {
+ content += '\n```'
}
+ replaceAll(content)
+ }
})
.fail(function (data) {
- showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false);
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false)
})
.always(function () {
- ui.spinner.hide();
- });
+ ui.spinner.hide()
+ })
})
.fail(function (data) {
- showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false);
- });
- }
-});
-
-//snippet export modal
-$("#snippetExportModalConfirm").click(function() {
- var accesstoken = $("#snippetExportModalAccessToken").val(),
- baseURL = $("#snippetExportModalBaseURL").val(),
- data = {
- title: $("#snippetExportModalTitle").val(),
- file_name: $("#snippetExportModalFileName").val(),
- code: editor.getValue(),
- visibility_level: $("#snippetExportModalVisibility").val()
- };
- if (!data.title || !data.file_name || !data.code || !data.visibility_level || !$("#snippetExportModalProjects").val()) return;
- $("#snippetExportModalLoading").show();
- var fullURL = baseURL + '/api/v3/projects/' + $("#snippetExportModalProjects").val() + '/snippets?access_token=' + accesstoken;
- $.post(fullURL
+ showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false)
+ })
+ }
+})
+
+// snippet export modal
+$('#snippetExportModalConfirm').click(function () {
+ var accesstoken = $('#snippetExportModalAccessToken').val()
+ var baseURL = $('#snippetExportModalBaseURL').val()
+ var data = {
+ title: $('#snippetExportModalTitle').val(),
+ file_name: $('#snippetExportModalFileName').val(),
+ code: editor.getValue(),
+ visibility_level: $('#snippetExportModalVisibility').val()
+ }
+ if (!data.title || !data.file_name || !data.code || !data.visibility_level || !$('#snippetExportModalProjects').val()) return
+ $('#snippetExportModalLoading').show()
+ var fullURL = baseURL + '/api/v3/projects/' + $('#snippetExportModalProjects').val() + '/snippets?access_token=' + accesstoken
+ $.post(fullURL
, data
- , function(ret) {
- $("#snippetExportModalLoading").hide();
- $('#snippetExportModal').modal('hide');
- var redirect = baseURL + '/' + $("#snippetExportModalProjects option[value='" + $("#snippetExportModalProjects").val() + "']").text() + '/snippets/' + ret.id;
- showMessageModal('<i class="fa fa-gitlab"></i> Export to Snippet', 'Export Successful!', redirect, 'View Snippet Here', true);
+ , function (ret) {
+ $('#snippetExportModalLoading').hide()
+ $('#snippetExportModal').modal('hide')
+ var redirect = baseURL + '/' + $("#snippetExportModalProjects option[value='" + $('#snippetExportModalProjects').val() + "']").text() + '/snippets/' + ret.id
+ showMessageModal('<i class="fa fa-gitlab"></i> Export to Snippet', 'Export Successful!', redirect, 'View Snippet Here', true)
}
, 'json'
- );
-});
-
-function parseToEditor(data) {
- var parsed = toMarkdown(data);
- if (parsed)
- replaceAll(parsed);
-}
-
-function replaceAll(data) {
- editor.replaceRange(data, {
- line: 0,
- ch: 0
- }, {
- line: editor.lastLine(),
- ch: editor.lastLine().length
- }, '+input');
-}
-
-function importFromUrl(url) {
- //console.log(url);
- if (!url) return;
- if (!isValidURL(url)) {
- showMessageModal('<i class="fa fa-cloud-download"></i> Import from URL', 'Not a valid URL :(', '', '', false);
- return;
- }
- $.ajax({
- method: "GET",
- url: url,
- success: function (data) {
- var extension = url.split('.').pop();
- if (extension == 'html')
- parseToEditor(data);
- else
- replaceAll(data);
- },
- error: function (data) {
- showMessageModal('<i class="fa fa-cloud-download"></i> Import from URL', 'Import failed :(', '', JSON.stringify(data), false);
- },
- complete: function () {
- ui.spinner.hide();
- }
- });
+ )
+})
+
+function parseToEditor (data) {
+ var parsed = toMarkdown(data)
+ if (parsed) { replaceAll(parsed) }
+}
+
+function replaceAll (data) {
+ editor.replaceRange(data, {
+ line: 0,
+ ch: 0
+ }, {
+ line: editor.lastLine(),
+ ch: editor.lastLine().length
+ }, '+input')
+}
+
+function importFromUrl (url) {
+ // console.log(url);
+ if (!url) return
+ if (!isValidURL(url)) {
+ showMessageModal('<i class="fa fa-cloud-download"></i> Import from URL', 'Not a valid URL :(', '', '', false)
+ return
+ }
+ $.ajax({
+ method: 'GET',
+ url: url,
+ success: function (data) {
+ var extension = url.split('.').pop()
+ if (extension === 'html') { parseToEditor(data) } else { replaceAll(data) }
+ },
+ error: function (data) {
+ showMessageModal('<i class="fa fa-cloud-download"></i> Import from URL', 'Import failed :(', '', JSON.stringify(data), false)
+ },
+ complete: function () {
+ ui.spinner.hide()
+ }
+ })
}
-//mode
+// mode
ui.toolbar.mode.click(function () {
- toggleMode();
-});
-//edit
+ toggleMode()
+})
+// edit
ui.toolbar.edit.click(function () {
- changeMode(modeType.edit);
-});
-//view
+ changeMode(modeType.edit)
+})
+// view
ui.toolbar.view.click(function () {
- changeMode(modeType.view);
-});
-//both
+ changeMode(modeType.view)
+})
+// both
ui.toolbar.both.click(function () {
- changeMode(modeType.both);
-});
-//permission
-//freely
+ changeMode(modeType.both)
+})
+// permission
+// freely
ui.infobar.permission.freely.click(function () {
- emitPermission("freely");
-});
-//editable
+ emitPermission('freely')
+})
+// editable
ui.infobar.permission.editable.click(function () {
- emitPermission("editable");
-});
-//locked
+ emitPermission('editable')
+})
+// locked
ui.infobar.permission.locked.click(function () {
- emitPermission("locked");
-});
-//private
+ emitPermission('locked')
+})
+// private
ui.infobar.permission.private.click(function () {
- emitPermission("private");
-});
-//limited
-ui.infobar.permission.limited.click(function() {
- emitPermission("limited");
-});
-//protected
-ui.infobar.permission.protected.click(function() {
- emitPermission("protected");
-});
+ emitPermission('private')
+})
+// limited
+ui.infobar.permission.limited.click(function () {
+ emitPermission('limited')
+})
+// protected
+ui.infobar.permission.protected.click(function () {
+ emitPermission('protected')
+})
// delete note
ui.infobar.delete.click(function () {
- $('.delete-modal').modal('show');
-});
+ $('.delete-modal').modal('show')
+})
$('.ui-delete-modal-confirm').click(function () {
- socket.emit('delete');
-});
-
-function emitPermission(_permission) {
- if (_permission != permission) {
- socket.emit('permission', _permission);
- }
-}
-
-function updatePermission(newPermission) {
- if (permission != newPermission) {
- permission = newPermission;
- if (loaded) refreshView();
- }
- 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-shield"></i> Editable';
- title = "Signed people can edit";
- break;
- case "limited":
- label = '<i class="fa fa-id-card"></i> Limited';
- title = "Signed people can edit (forbid guest)"
- break;
- case "locked":
- label = '<i class="fa fa-lock"></i> Locked';
- title = "Only owner can edit";
- break;
- case "protected":
- label = '<i class="fa fa-umbrella"></i> Protected';
- title = "Only owner can edit (forbid guest)";
- break;
- case "private":
- label = '<i class="fa fa-hand-stop-o"></i> Private';
- title = "Only owner can view & edit";
- break;
- }
- if (personalInfo.userid && owner && 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);
-}
-
-function havePermission() {
- var bool = false;
- switch (permission) {
- case "freely":
- bool = true;
- break;
- case "editable":
- case "limited":
- if (!personalInfo.login) {
- bool = false;
- } else {
- bool = true;
- }
- break;
- case "locked":
- case "private":
- case "protected":
- if (!owner || personalInfo.userid != owner) {
- bool = false;
- } else {
- bool = true;
- }
- break;
- }
- return bool;
+ socket.emit('delete')
+})
+
+function emitPermission (_permission) {
+ if (_permission !== permission) {
+ socket.emit('permission', _permission)
+ }
+}
+
+function updatePermission (newPermission) {
+ if (permission !== newPermission) {
+ permission = newPermission
+ if (window.loaded) refreshView()
+ }
+ 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-shield"></i> Editable'
+ title = 'Signed people can edit'
+ break
+ case 'limited':
+ label = '<i class="fa fa-id-card"></i> Limited'
+ title = 'Signed people can edit (forbid guest)'
+ break
+ case 'locked':
+ label = '<i class="fa fa-lock"></i> Locked'
+ title = 'Only owner can edit'
+ break
+ case 'protected':
+ label = '<i class="fa fa-umbrella"></i> Protected'
+ title = 'Only owner can edit (forbid guest)'
+ break
+ case 'private':
+ label = '<i class="fa fa-hand-stop-o"></i> Private'
+ title = 'Only owner can view & edit'
+ break
+ }
+ if (window.personalInfo.userid && window.owner && window.personalInfo.userid === window.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)
+}
+
+function havePermission () {
+ var bool = false
+ switch (permission) {
+ case 'freely':
+ bool = true
+ break
+ case 'editable':
+ case 'limited':
+ if (!window.personalInfo.login) {
+ bool = false
+ } else {
+ bool = true
+ }
+ break
+ case 'locked':
+ case 'private':
+ case 'protected':
+ if (!window.owner || window.personalInfo.userid !== window.owner) {
+ bool = false
+ } else {
+ bool = true
+ }
+ break
+ }
+ return bool
}
// global module workaround
-window.havePermission = havePermission;
+window.havePermission = havePermission
-//socket.io actions
-var io = require("socket.io-client");
+// socket.io actions
+var io = require('socket.io-client')
var socket = io.connect({
- path: urlpath ? '/' + urlpath + '/socket.io/' : '',
- timeout: 5000, //5 secs to timeout,
- reconnectionAttempts: 20 // retry 20 times on connect failed
-});
-//overwrite original event for checking login state
-var on = socket.on;
+ path: urlpath ? '/' + urlpath + '/socket.io/' : '',
+ timeout: 5000, // 5 secs to timeout,
+ reconnectionAttempts: 20 // retry 20 times on connect failed
+})
+// overwrite original event for checking login state
+var on = socket.on
socket.on = function () {
- if (!checkLoginStateChanged() && !needRefresh)
- return on.apply(socket, arguments);
-};
-var emit = socket.emit;
+ if (!checkLoginStateChanged() && !window.needRefresh) { return on.apply(socket, arguments) }
+}
+var emit = socket.emit
socket.emit = function () {
- if (!checkLoginStateChanged() && !needRefresh)
- emit.apply(socket, arguments);
-};
+ if (!checkLoginStateChanged() && !window.needRefresh) { emit.apply(socket, arguments) }
+}
socket.on('info', function (data) {
- console.error(data);
- switch (data.code) {
- case 403:
- location.href = serverurl + "/403";
- break;
- case 404:
- location.href = serverurl + "/404";
- break;
- case 500:
- location.href = serverurl + "/500";
- break;
- }
-});
+ console.error(data)
+ switch (data.code) {
+ case 403:
+ location.href = serverurl + '/403'
+ break
+ case 404:
+ location.href = serverurl + '/404'
+ break
+ case 500:
+ location.href = serverurl + '/500'
+ break
+ }
+})
socket.on('error', function (data) {
- console.error(data);
- if (data.message && data.message.indexOf('AUTH failed') === 0)
- location.href = serverurl + "/403";
-});
+ console.error(data)
+ if (data.message && data.message.indexOf('AUTH failed') === 0) { location.href = serverurl + '/403' }
+})
socket.on('delete', function () {
- if (personalInfo.login) {
- deleteServerHistory(noteid, function (err, data) {
- if (!err) location.href = serverurl;
- });
- } else {
- getHistory(function (notehistory) {
- var newnotehistory = removeHistory(noteid, notehistory);
- saveHistory(newnotehistory);
- location.href = serverurl;
- });
- }
-});
-var retryTimer = null;
+ if (window.personalInfo.login) {
+ deleteServerHistory(noteid, function (err, data) {
+ if (!err) location.href = serverurl
+ })
+ } else {
+ getHistory(function (notehistory) {
+ var newnotehistory = removeHistory(noteid, notehistory)
+ saveHistory(newnotehistory)
+ location.href = serverurl
+ })
+ }
+})
+var retryTimer = null
socket.on('maintenance', function () {
- cmClient.revision = -1;
-});
+ cmClient.revision = -1
+})
socket.on('disconnect', function (data) {
- showStatus(statusType.offline);
- if (loaded) {
- saveInfo();
- lastInfo.history = editor.getHistory();
- }
- if (!editor.getOption('readOnly'))
- editor.setOption('readOnly', true);
- if (!retryTimer) {
- retryTimer = setInterval(function () {
- if (!needRefresh) socket.connect();
- }, 1000);
- }
-});
+ showStatus(statusType.offline)
+ if (window.loaded) {
+ saveInfo()
+ window.lastInfo.history = editor.getHistory()
+ }
+ if (!editor.getOption('readOnly')) { editor.setOption('readOnly', true) }
+ if (!retryTimer) {
+ retryTimer = setInterval(function () {
+ if (!window.needRefresh) socket.connect()
+ }, 1000)
+ }
+})
socket.on('reconnect', function (data) {
- //sync back any change in offline
- emitUserStatus(true);
- cursorActivity();
- socket.emit('online users');
-});
+ // sync back any change in offline
+ emitUserStatus(true)
+ cursorActivity()
+ socket.emit('online users')
+})
socket.on('connect', function (data) {
- clearInterval(retryTimer);
- retryTimer = null;
- personalInfo['id'] = socket.id;
- showStatus(statusType.connected);
- socket.emit('version');
-});
+ clearInterval(retryTimer)
+ retryTimer = null
+ window.personalInfo['id'] = socket.id
+ showStatus(statusType.connected)
+ socket.emit('version')
+})
socket.on('version', function (data) {
- if (version != data.version) {
- if (version < data.minimumCompatibleVersion) {
- setRefreshModal('incompatible-version');
- setNeedRefresh();
- } else {
- setRefreshModal('new-version');
- }
- }
-});
-var authors = [];
-var authorship = [];
-var authorshipMarks = {};
-var authorMarks = {}; // temp variable
-var addTextMarkers = []; // temp variable
-function updateInfo(data) {
- //console.log(data);
- if (data.hasOwnProperty('createtime') && createtime !== data.createtime) {
- createtime = data.createtime;
- updateLastChange();
- }
- if (data.hasOwnProperty('updatetime') && lastchangetime !== data.updatetime) {
- lastchangetime = data.updatetime;
- updateLastChange();
- }
- if (data.hasOwnProperty('owner') && owner !== data.owner) {
- owner = data.owner;
- ownerprofile = data.ownerprofile;
- updateOwner();
- }
- if (data.hasOwnProperty('lastchangeuser') && lastchangeuser !== data.lastchangeuser) {
- lastchangeuser = data.lastchangeuser;
- lastchangeuserprofile = data.lastchangeuserprofile;
- updateLastChangeUser();
- updateOwner();
- }
- if (data.hasOwnProperty('authors') && authors !== data.authors) {
- authors = data.authors;
- }
- if (data.hasOwnProperty('authorship') && authorship !== data.authorship) {
- authorship = data.authorship;
- updateAuthorship();
+ if (version !== data.version) {
+ if (version < data.minimumCompatibleVersion) {
+ setRefreshModal('incompatible-version')
+ setNeedRefresh()
+ } else {
+ setRefreshModal('new-version')
}
+ }
+})
+var authors = []
+var authorship = []
+var authorMarks = {} // temp variable
+var addTextMarkers = [] // temp variable
+function updateInfo (data) {
+ // console.log(data);
+ if (data.hasOwnProperty('createtime') && window.createtime !== data.createtime) {
+ window.createtime = data.createtime
+ updateLastChange()
+ }
+ if (data.hasOwnProperty('updatetime') && window.lastchangetime !== data.updatetime) {
+ window.lastchangetime = data.updatetime
+ updateLastChange()
+ }
+ if (data.hasOwnProperty('owner') && window.owner !== data.owner) {
+ window.owner = data.owner
+ window.ownerprofile = data.ownerprofile
+ updateOwner()
+ }
+ if (data.hasOwnProperty('lastchangeuser') && window.lastchangeuser !== data.lastchangeuser) {
+ window.lastchangeuser = data.lastchangeuser
+ window.lastchangeuserprofile = data.lastchangeuserprofile
+ updateLastChangeUser()
+ updateOwner()
+ }
+ if (data.hasOwnProperty('authors') && authors !== data.authors) {
+ authors = data.authors
+ }
+ if (data.hasOwnProperty('authorship') && authorship !== data.authorship) {
+ authorship = data.authorship
+ updateAuthorship()
+ }
}
var updateAuthorship = _.debounce(function () {
- editor.operation(updateAuthorshipInner);
-}, 50);
-function initMark() {
- return {
- gutter: {
- userid: null,
- timestamp: null
- },
- textmarkers: []
- };
-}
-function initMarkAndCheckGutter(mark, author, timestamp) {
- if (!mark) mark = initMark();
- if (!mark.gutter.userid || mark.gutter.timestamp > timestamp) {
- mark.gutter.userid = author.userid;
- mark.gutter.timestamp = timestamp;
- }
- return mark;
-}
-var gutterStylePrefix = "border-left: 3px solid ";
-var gutterStylePostfix = "; height: " + defaultTextHeight + "px; margin-left: 3px;";
-var textMarkderStylePrefix = "background-image: linear-gradient(to top, ";
-var textMarkderStylePostfix = " 1px, transparent 1px);";
+ editor.operation(updateAuthorshipInner)
+}, 50)
+function initMark () {
+ return {
+ gutter: {
+ userid: null,
+ timestamp: null
+ },
+ textmarkers: []
+ }
+}
+function initMarkAndCheckGutter (mark, author, timestamp) {
+ if (!mark) mark = initMark()
+ if (!mark.gutter.userid || mark.gutter.timestamp > timestamp) {
+ mark.gutter.userid = author.userid
+ mark.gutter.timestamp = timestamp
+ }
+ return mark
+}
+var gutterStylePrefix = 'border-left: 3px solid '
+var gutterStylePostfix = '; height: ' + defaultTextHeight + 'px; margin-left: 3px;'
+var textMarkderStylePrefix = 'background-image: linear-gradient(to top, '
+var textMarkderStylePostfix = ' 1px, transparent 1px);'
var addStyleRule = (function () {
- var added = {};
- var styleElement = document.createElement('style');
- document.documentElement.getElementsByTagName('head')[0].appendChild(styleElement);
- var styleSheet = styleElement.sheet;
-
- return function (css) {
- if (added[css]) {
- return;
- }
- added[css] = true;
- styleSheet.insertRule(css, (styleSheet.cssRules || styleSheet.rules).length);
- };
-}());
-function updateAuthorshipInner() {
+ var added = {}
+ var styleElement = document.createElement('style')
+ document.documentElement.getElementsByTagName('head')[0].appendChild(styleElement)
+ var styleSheet = styleElement.sheet
+
+ return function (css) {
+ if (added[css]) {
+ return
+ }
+ added[css] = true
+ styleSheet.insertRule(css, (styleSheet.cssRules || styleSheet.rules).length)
+ }
+}())
+function updateAuthorshipInner () {
// ignore when ot not synced yet
- if (havePendingOperation()) return;
- authorMarks = {};
- for (var i = 0; i < authorship.length; i++) {
- var atom = authorship[i];
- var author = authors[atom[0]];
- if (author) {
- var prePos = editor.posFromIndex(atom[1]);
- var preLine = editor.getLine(prePos.line);
- var postPos = editor.posFromIndex(atom[2]);
- var postLine = editor.getLine(postPos.line);
- if (prePos.ch == 0 && postPos.ch == postLine.length) {
- for (var j = prePos.line; j <= postPos.line; j++) {
- if (editor.getLine(j)) {
- authorMarks[j] = initMarkAndCheckGutter(authorMarks[j], author, atom[3]);
- }
- }
- } else if (postPos.line - prePos.line >= 1) {
- var startLine = prePos.line;
- var endLine = postPos.line;
- if (prePos.ch == preLine.length) {
- startLine++;
- } else if (prePos.ch != 0) {
- var mark = initMarkAndCheckGutter(authorMarks[prePos.line], author, atom[3]);
- var _postPos = {
- line: prePos.line,
- ch: preLine.length
- };
- if (JSON.stringify(prePos) != JSON.stringify(_postPos)) {
- mark.textmarkers.push({
- userid: author.userid,
- pos: [prePos, _postPos]
- });
- startLine++;
- }
- authorMarks[prePos.line] = mark;
- }
- if (postPos.ch == 0) {
- endLine--;
- } else if (postPos.ch != postLine.length) {
- var mark = initMarkAndCheckGutter(authorMarks[postPos.line], author, atom[3]);
- var _prePos = {
- line: postPos.line,
- ch: 0
- };
- if (JSON.stringify(_prePos) != JSON.stringify(postPos)) {
- mark.textmarkers.push({
- userid: author.userid,
- pos: [_prePos, postPos]
- });
- endLine--;
- }
- authorMarks[postPos.line] = mark;
- }
- for (var j = startLine; j <= endLine; j++) {
- if (editor.getLine(j)) {
- authorMarks[j] = initMarkAndCheckGutter(authorMarks[j], author, atom[3]);
- }
- }
- } else {
- var mark = initMarkAndCheckGutter(authorMarks[prePos.line], author, atom[3]);
- if (JSON.stringify(prePos) != JSON.stringify(postPos)) {
- mark.textmarkers.push({
- userid: author.userid,
- pos: [prePos, postPos]
- });
- }
- authorMarks[prePos.line] = mark;
- }
- }
- }
- addTextMarkers = [];
- editor.eachLine(iterateLine);
- var allTextMarks = editor.getAllMarks();
- for (var i = 0; i < allTextMarks.length; i++) {
- var _textMarker = allTextMarks[i];
- var pos = _textMarker.find();
- var found = false;
- for (var j = 0; j < addTextMarkers.length; j++) {
- var textMarker = addTextMarkers[j];
- var author = authors[textMarker.userid];
- var className = 'authorship-inline-' + author.color.substr(1);
- var obj = {
- from: textMarker.pos[0],
- to: textMarker.pos[1]
- };
- if (JSON.stringify(pos) == JSON.stringify(obj) && _textMarker.className &&
+ if (havePendingOperation()) return
+ authorMarks = {}
+ for (let i = 0; i < authorship.length; i++) {
+ var atom = authorship[i]
+ let author = authors[atom[0]]
+ if (author) {
+ var prePos = editor.posFromIndex(atom[1])
+ var preLine = editor.getLine(prePos.line)
+ var postPos = editor.posFromIndex(atom[2])
+ var postLine = editor.getLine(postPos.line)
+ if (prePos.ch === 0 && postPos.ch === postLine.length) {
+ for (let j = prePos.line; j <= postPos.line; j++) {
+ if (editor.getLine(j)) {
+ authorMarks[j] = initMarkAndCheckGutter(authorMarks[j], author, atom[3])
+ }
+ }
+ } else if (postPos.line - prePos.line >= 1) {
+ var startLine = prePos.line
+ var endLine = postPos.line
+ if (prePos.ch === preLine.length) {
+ startLine++
+ } else if (prePos.ch !== 0) {
+ let mark = initMarkAndCheckGutter(authorMarks[prePos.line], author, atom[3])
+ var _postPos = {
+ line: prePos.line,
+ ch: preLine.length
+ }
+ if (JSON.stringify(prePos) !== JSON.stringify(_postPos)) {
+ mark.textmarkers.push({ userid: author.userid, pos: [prePos, _postPos] })
+ startLine++
+ }
+ authorMarks[prePos.line] = mark
+ }
+ if (postPos.ch === 0) {
+ endLine--
+ } else if (postPos.ch !== postLine.length) {
+ let mark = initMarkAndCheckGutter(authorMarks[postPos.line], author, atom[3])
+ var _prePos = {
+ line: postPos.line,
+ ch: 0
+ }
+ if (JSON.stringify(_prePos) !== JSON.stringify(postPos)) {
+ mark.textmarkers.push({ userid: author.userid, pos: [_prePos, postPos] })
+ endLine--
+ }
+ authorMarks[postPos.line] = mark
+ }
+ for (let j = startLine; j <= endLine; j++) {
+ if (editor.getLine(j)) {
+ authorMarks[j] = initMarkAndCheckGutter(authorMarks[j], author, atom[3])
+ }
+ }
+ } else {
+ let mark = initMarkAndCheckGutter(authorMarks[prePos.line], author, atom[3])
+ if (JSON.stringify(prePos) !== JSON.stringify(postPos)) {
+ mark.textmarkers.push({
+ userid: author.userid,
+ pos: [prePos, postPos]
+ })
+ }
+ authorMarks[prePos.line] = mark
+ }
+ }
+ }
+ addTextMarkers = []
+ editor.eachLine(iterateLine)
+ var allTextMarks = editor.getAllMarks()
+ for (let i = 0; i < allTextMarks.length; i++) {
+ let _textMarker = allTextMarks[i]
+ var pos = _textMarker.find()
+ var found = false
+ for (let j = 0; j < addTextMarkers.length; j++) {
+ let textMarker = addTextMarkers[j]
+ let author = authors[textMarker.userid]
+ let className = 'authorship-inline-' + author.color.substr(1)
+ var obj = {
+ from: textMarker.pos[0],
+ to: textMarker.pos[1]
+ }
+ if (JSON.stringify(pos) === JSON.stringify(obj) && _textMarker.className &&
_textMarker.className.indexOf(className) > -1) {
- addTextMarkers.splice(j, 1);
- j--;
- found = true;
- break;
- }
- }
- if (!found && _textMarker.className && _textMarker.className.indexOf('authorship-inline') > -1) {
- _textMarker.clear();
- }
- }
- for (var i = 0; i < addTextMarkers.length; i++) {
- var textMarker = addTextMarkers[i];
- var author = authors[textMarker.userid];
- var rgbcolor = hex2rgb(author.color);
- var colorString = "rgba(" + rgbcolor.red + "," + rgbcolor.green + "," + rgbcolor.blue + ",0.7)";
- var styleString = textMarkderStylePrefix + colorString + textMarkderStylePostfix;
- var className = 'authorship-inline-' + author.color.substr(1);
- var rule = "." + className + "{" + styleString + "}";
- addStyleRule(rule);
- var _textMarker = editor.markText(textMarker.pos[0], textMarker.pos[1], {
- className: 'authorship-inline ' + className,
- title: author.name
- });
- }
- authorshipMarks = authorMarks;
-}
-function iterateLine(line) {
- var lineNumber = line.lineNo();
- var currMark = authorMarks[lineNumber];
- var author = currMark ? authors[currMark.gutter.userid] : null;
- if (currMark && author) {
- var className = 'authorship-gutter-' + author.color.substr(1);
- var gutters = line.gutterMarkers;
- if (!gutters || !gutters['authorship-gutters'] ||
+ addTextMarkers.splice(j, 1)
+ j--
+ found = true
+ break
+ }
+ }
+ if (!found && _textMarker.className && _textMarker.className.indexOf('authorship-inline') > -1) {
+ _textMarker.clear()
+ }
+ }
+ for (let i = 0; i < addTextMarkers.length; i++) {
+ let textMarker = addTextMarkers[i]
+ let author = authors[textMarker.userid]
+ var rgbcolor = hex2rgb(author.color)
+ var colorString = 'rgba(' + rgbcolor.red + ',' + rgbcolor.green + ',' + rgbcolor.blue + ',0.7)'
+ var styleString = textMarkderStylePrefix + colorString + textMarkderStylePostfix
+ let className = 'authorship-inline-' + author.color.substr(1)
+ var rule = '.' + className + '{' + styleString + '}'
+ addStyleRule(rule)
+ editor.markText(textMarker.pos[0], textMarker.pos[1], {
+ className: 'authorship-inline ' + className,
+ title: author.name
+ })
+ }
+}
+function iterateLine (line) {
+ var lineNumber = line.lineNo()
+ var currMark = authorMarks[lineNumber]
+ var author = currMark ? authors[currMark.gutter.userid] : null
+ if (currMark && author) {
+ let className = 'authorship-gutter-' + author.color.substr(1)
+ var gutters = line.gutterMarkers
+ if (!gutters || !gutters['authorship-gutters'] ||
!gutters['authorship-gutters'].className ||
!gutters['authorship-gutters'].className.indexOf(className) < 0) {
- var styleString = gutterStylePrefix + author.color + gutterStylePostfix;
- var rule = "." + className + "{" + styleString + "}";
- addStyleRule(rule);
- var gutter = $('<div>', {
- class: 'authorship-gutter ' + className,
- title: author.name
- });
- editor.setGutterMarker(line, "authorship-gutters", gutter[0]);
- }
- } else {
- editor.setGutterMarker(line, "authorship-gutters", null);
- }
- if (currMark && currMark.textmarkers.length > 0) {
- for (var i = 0; i < currMark.textmarkers.length; i++) {
- var textMarker = currMark.textmarkers[i];
- if (textMarker.userid != currMark.gutter.userid) {
- addTextMarkers.push(textMarker);
- }
- }
- }
+ var styleString = gutterStylePrefix + author.color + gutterStylePostfix
+ var rule = '.' + className + '{' + styleString + '}'
+ addStyleRule(rule)
+ var gutter = $('<div>', {
+ class: 'authorship-gutter ' + className,
+ title: author.name
+ })
+ editor.setGutterMarker(line, 'authorship-gutters', gutter[0])
+ }
+ } else {
+ editor.setGutterMarker(line, 'authorship-gutters', null)
+ }
+ if (currMark && currMark.textmarkers.length > 0) {
+ for (var i = 0; i < currMark.textmarkers.length; i++) {
+ let textMarker = currMark.textmarkers[i]
+ if (textMarker.userid !== currMark.gutter.userid) {
+ addTextMarkers.push(textMarker)
+ }
+ }
+ }
}
editor.on('update', function () {
- $('.authorship-gutter:not([data-original-title])').tooltip({
- container: '.CodeMirror-lines',
- placement: 'right',
- delay: { "show": 500, "hide": 100 }
- });
- $('.authorship-inline:not([data-original-title])').tooltip({
- container: '.CodeMirror-lines',
- placement: 'bottom',
- delay: { "show": 500, "hide": 100 }
- });
+ $('.authorship-gutter:not([data-original-title])').tooltip({
+ container: '.CodeMirror-lines',
+ placement: 'right',
+ delay: { 'show': 500, 'hide': 100 }
+ })
+ $('.authorship-inline:not([data-original-title])').tooltip({
+ container: '.CodeMirror-lines',
+ placement: 'bottom',
+ delay: { 'show': 500, 'hide': 100 }
+ })
// clear tooltip which described element has been removed
- $('[id^="tooltip"]').each(function (index, element) {
- var $ele = $(element);
- if ($('[aria-describedby="' + $ele.attr('id') + '"]').length <= 0) $ele.remove();
- });
-});
+ $('[id^="tooltip"]').each(function (index, element) {
+ var $ele = $(element)
+ if ($('[aria-describedby="' + $ele.attr('id') + '"]').length <= 0) $ele.remove()
+ })
+})
socket.on('check', function (data) {
- //console.log(data);
- updateInfo(data);
-});
+ // console.log(data);
+ updateInfo(data)
+})
socket.on('permission', function (data) {
- updatePermission(data.permission);
-});
-var docmaxlength = null;
-var permission = null;
+ updatePermission(data.permission)
+})
+var docmaxlength = null
+var permission = null
socket.on('refresh', function (data) {
- //console.log(data);
- docmaxlength = data.docmaxlength;
- editor.setOption("maxLength", docmaxlength);
- updateInfo(data);
- updatePermission(data.permission);
- if (!loaded) {
+ // console.log(data);
+ docmaxlength = data.docmaxlength
+ editor.setOption('maxLength', docmaxlength)
+ updateInfo(data)
+ updatePermission(data.permission)
+ if (!window.loaded) {
// auto change mode if no content detected
- var nocontent = editor.getValue().length <= 0;
- if (nocontent) {
- if (visibleXS)
- currentMode = modeType.edit;
- else
- currentMode = modeType.both;
- }
+ var nocontent = editor.getValue().length <= 0
+ if (nocontent) {
+ if (window.visibleXS) { window.currentMode = modeType.edit } else { window.currentMode = modeType.both }
+ }
// parse mode from url
- if (window.location.search.length > 0) {
- var urlMode = modeType[window.location.search.substr(1)];
- if (urlMode) currentMode = urlMode;
- }
- changeMode(currentMode);
- if (nocontent && !visibleXS) {
- editor.focus();
- editor.refresh();
- }
- updateViewInner(); // bring up view rendering earlier
- updateHistory(); //update history whether have content or not
- loaded = true;
- emitUserStatus(); //send first user status
- updateOnlineStatus(); //update first online status
- setTimeout(function () {
- //work around editor not refresh or doc not fully loaded
- windowResizeInner();
- //work around might not scroll to hash
- scrollToHash();
- }, 1);
- }
- if (editor.getOption('readOnly'))
- editor.setOption('readOnly', false);
-});
-
-var EditorClient = ot.EditorClient;
-var SocketIOAdapter = ot.SocketIOAdapter;
-var CodeMirrorAdapter = ot.CodeMirrorAdapter;
-var cmClient = null;
-var synchronized_ = null;
-
-function havePendingOperation() {
- return (cmClient && cmClient.state && cmClient.state.hasOwnProperty('outstanding')) ? true : false;
-}
-
-socket.on('doc', function (obj) {
- var body = obj.str;
- var bodyMismatch = editor.getValue() !== body;
- var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== obj.revision && !havePendingOperation()))) || obj.force;
+ if (window.location.search.length > 0) {
+ var urlMode = modeType[window.location.search.substr(1)]
+ if (urlMode) window.currentMode = urlMode
+ }
+ changeMode(window.currentMode)
+ if (nocontent && !window.visibleXS) {
+ editor.focus()
+ editor.refresh()
+ }
+ updateViewInner() // bring up view rendering earlier
+ updateHistory() // update history whether have content or not
+ window.loaded = true
+ emitUserStatus() // send first user status
+ updateOnlineStatus() // update first online status
+ setTimeout(function () {
+ // work around editor not refresh or doc not fully loaded
+ windowResizeInner()
+ // work around might not scroll to hash
+ scrollToHash()
+ }, 1)
+ }
+ if (editor.getOption('readOnly')) { editor.setOption('readOnly', false) }
+})
- saveInfo();
- if (setDoc && bodyMismatch) {
- if (cmClient) cmClient.editorAdapter.ignoreNextChange = true;
- if (body) editor.setValue(body);
- else editor.setValue("");
- }
+var EditorClient = ot.EditorClient
+var SocketIOAdapter = ot.SocketIOAdapter
+var CodeMirrorAdapter = ot.CodeMirrorAdapter
+var cmClient = null
+var synchronized_ = null
- if (!loaded) {
- editor.clearHistory();
- ui.spinner.hide();
- ui.content.fadeIn();
- } else {
- //if current doc is equal to the doc before disconnect
- if (setDoc && bodyMismatch) editor.clearHistory();
- else if (lastInfo.history) editor.setHistory(lastInfo.history);
- lastInfo.history = null;
- }
+function havePendingOperation () {
+ return !!((cmClient && cmClient.state && cmClient.state.hasOwnProperty('outstanding')))
+}
- if (!cmClient) {
- cmClient = window.cmClient = new EditorClient(
+socket.on('doc', function (obj) {
+ var body = obj.str
+ var bodyMismatch = editor.getValue() !== body
+ var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== obj.revision && !havePendingOperation()))) || obj.force
+
+ saveInfo()
+ if (setDoc && bodyMismatch) {
+ if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
+ if (body) editor.setValue(body)
+ else editor.setValue('')
+ }
+
+ if (!window.loaded) {
+ editor.clearHistory()
+ ui.spinner.hide()
+ ui.content.fadeIn()
+ } else {
+ // if current doc is equal to the doc before disconnect
+ if (setDoc && bodyMismatch) editor.clearHistory()
+ else if (window.lastInfo.history) editor.setHistory(window.lastInfo.history)
+ window.lastInfo.history = null
+ }
+
+ if (!cmClient) {
+ cmClient = window.cmClient = new EditorClient(
obj.revision, obj.clients,
new SocketIOAdapter(socket), new CodeMirrorAdapter(editor)
- );
- synchronized_ = cmClient.state;
- } else if (setDoc) {
- if (bodyMismatch) {
- cmClient.undoManager.undoStack.length = 0;
- cmClient.undoManager.redoStack.length = 0;
- }
- cmClient.revision = obj.revision;
- cmClient.setState(synchronized_);
- cmClient.initializeClientList();
- cmClient.initializeClients(obj.clients);
- } else if (havePendingOperation()) {
- cmClient.serverReconnect();
- }
-
- if (setDoc && bodyMismatch) {
- isDirty = true;
- updateView();
- }
-
- restoreInfo();
-});
+ )
+ synchronized_ = cmClient.state
+ } else if (setDoc) {
+ if (bodyMismatch) {
+ cmClient.undoManager.undoStack.length = 0
+ cmClient.undoManager.redoStack.length = 0
+ }
+ cmClient.revision = obj.revision
+ cmClient.setState(synchronized_)
+ cmClient.initializeClientList()
+ cmClient.initializeClients(obj.clients)
+ } else if (havePendingOperation()) {
+ cmClient.serverReconnect()
+ }
+
+ if (setDoc && bodyMismatch) {
+ window.isDirty = true
+ updateView()
+ }
+
+ restoreInfo()
+})
socket.on('ack', function () {
- isDirty = true;
- updateView();
-});
+ window.isDirty = true
+ updateView()
+})
socket.on('operation', function () {
- isDirty = true;
- updateView();
-});
+ window.isDirty = true
+ updateView()
+})
socket.on('online users', function (data) {
- if (debug)
- console.debug(data);
- onlineUsers = data.users;
- updateOnlineStatus();
- $('.CodeMirror-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).stop(true).fadeOut("normal", function () {
- $(this).remove();
- });
- });
+ if (debug) { console.debug(data) }
+ window.onlineUsers = data.users
+ updateOnlineStatus()
+ $('.CodeMirror-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 (user.id != socket.id)
- buildCursor(user);
- else
- personalInfo = user;
- }
-});
+ var user = data.users[i]
+ if ($(this).attr('id') === user.id) { found = true }
+ }
+ if (!found) {
+ $(this).stop(true).fadeOut('normal', function () {
+ $(this).remove()
+ })
+ }
+ })
+ for (var i = 0; i < data.users.length; i++) {
+ var user = data.users[i]
+ if (user.id !== socket.id) { buildCursor(user) } else { window.personalInfo = user }
+ }
+})
socket.on('user status', function (data) {
- if (debug)
- console.debug(data);
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == data.id) {
- onlineUsers[i] = data;
- }
- }
- updateOnlineStatus();
- if (data.id != socket.id)
- buildCursor(data);
-});
+ if (debug) { console.debug(data) }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === data.id) {
+ window.onlineUsers[i] = data
+ }
+ }
+ updateOnlineStatus()
+ if (data.id !== socket.id) { buildCursor(data) }
+})
socket.on('cursor focus', function (data) {
- if (debug)
- console.debug(data);
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == data.id) {
- onlineUsers[i].cursor = data.cursor;
- }
- }
- if (data.id != socket.id)
- buildCursor(data);
- //force show
- var cursor = $('div[data-clientid="' + data.id + '"]');
- if (cursor.length > 0) {
- cursor.stop(true).fadeIn();
- }
-});
+ if (debug) { console.debug(data) }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === data.id) {
+ window.onlineUsers[i].cursor = data.cursor
+ }
+ }
+ if (data.id !== socket.id) { buildCursor(data) }
+ // force show
+ var cursor = $('div[data-clientid="' + data.id + '"]')
+ if (cursor.length > 0) {
+ cursor.stop(true).fadeIn()
+ }
+})
socket.on('cursor activity', function (data) {
- if (debug)
- console.debug(data);
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == data.id) {
- onlineUsers[i].cursor = data.cursor;
- }
+ if (debug) { console.debug(data) }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === data.id) {
+ window.onlineUsers[i].cursor = data.cursor
}
- if (data.id != socket.id)
- buildCursor(data);
-});
+ }
+ if (data.id !== socket.id) { buildCursor(data) }
+})
socket.on('cursor blur', function (data) {
- if (debug)
- console.debug(data);
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == data.id) {
- onlineUsers[i].cursor = null;
- }
- }
- if (data.id != socket.id)
- buildCursor(data);
- //force hide
- var cursor = $('div[data-clientid="' + data.id + '"]');
- if (cursor.length > 0) {
- cursor.stop(true).fadeOut();
- }
-});
+ if (debug) { console.debug(data) }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === data.id) {
+ window.onlineUsers[i].cursor = null
+ }
+ }
+ if (data.id !== socket.id) { buildCursor(data) }
+ // force hide
+ var cursor = $('div[data-clientid="' + data.id + '"]')
+ if (cursor.length > 0) {
+ cursor.stop(true).fadeOut()
+ }
+})
var options = {
- valueNames: ['id', 'name'],
- item: '<li class="ui-user-item">\
- <span class="id" style="display:none;"></span>\
- <a href="#">\
- <span class="pull-left"><i class="ui-user-icon"></i></span><span class="ui-user-name name"></span><span class="pull-right"><i class="fa fa-circle ui-user-status"></i></span>\
- </a>\
- </li>'
-};
-var onlineUserList = new List('online-user-list', options);
-var shortOnlineUserList = new List('short-online-user-list', options);
-
-function updateOnlineStatus() {
- if (!loaded || !socket.connected) return;
- var _onlineUsers = deduplicateOnlineUsers(onlineUsers);
- showStatus(statusType.online, _onlineUsers.length);
- var items = onlineUserList.items;
- //update or remove current list items
- for (var i = 0; i < items.length; i++) {
- var found = false;
- var foundindex = null;
- for (var j = 0; j < _onlineUsers.length; j++) {
- if (items[i].values().id == _onlineUsers[j].id) {
- foundindex = j;
- found = true;
- break;
- }
- }
- var id = items[i].values().id;
- if (found) {
- onlineUserList.get('id', id)[0].values(_onlineUsers[foundindex]);
- shortOnlineUserList.get('id', id)[0].values(_onlineUsers[foundindex]);
- } else {
- onlineUserList.remove('id', id);
- shortOnlineUserList.remove('id', id);
- }
- }
- //add not in list items
- for (var i = 0; i < _onlineUsers.length; i++) {
- var found = false;
- for (var j = 0; j < items.length; j++) {
- if (items[j].values().id == _onlineUsers[i].id) {
- found = true;
- break;
- }
- }
- if (!found) {
- onlineUserList.add(_onlineUsers[i]);
- shortOnlineUserList.add(_onlineUsers[i]);
- }
+ valueNames: ['id', 'name'],
+ item: '<li class="ui-user-item">' +
+ '<span class="id" style="display:none;"></span>' +
+ '<a href="#">' +
+ '<span class="pull-left"><i class="ui-user-icon"></i></span><span class="ui-user-name name"></span><span class="pull-right"><i class="fa fa-circle ui-user-status"></i></span>' +
+ '</a>' +
+ '</li>'
+}
+var onlineUserList = new List('online-user-list', options)
+var shortOnlineUserList = new List('short-online-user-list', options)
+
+function updateOnlineStatus () {
+ if (!window.loaded || !socket.connected) return
+ var _onlineUsers = deduplicateOnlineUsers(window.onlineUsers)
+ showStatus(statusType.online, _onlineUsers.length)
+ var items = onlineUserList.items
+ // update or remove current list items
+ for (let i = 0; i < items.length; i++) {
+ let found = false
+ let foundindex = null
+ for (let j = 0; j < _onlineUsers.length; j++) {
+ if (items[i].values().id === _onlineUsers[j].id) {
+ foundindex = j
+ found = true
+ break
+ }
+ }
+ let id = items[i].values().id
+ if (found) {
+ onlineUserList.get('id', id)[0].values(_onlineUsers[foundindex])
+ shortOnlineUserList.get('id', id)[0].values(_onlineUsers[foundindex])
+ } else {
+ onlineUserList.remove('id', id)
+ shortOnlineUserList.remove('id', id)
+ }
+ }
+ // add not in list items
+ for (let i = 0; i < _onlineUsers.length; i++) {
+ let found = false
+ for (let j = 0; j < items.length; j++) {
+ if (items[j].values().id === _onlineUsers[i].id) {
+ found = true
+ break
+ }
+ }
+ if (!found) {
+ onlineUserList.add(_onlineUsers[i])
+ shortOnlineUserList.add(_onlineUsers[i])
+ }
+ }
+ // sorting
+ sortOnlineUserList(onlineUserList)
+ sortOnlineUserList(shortOnlineUserList)
+ // render list items
+ renderUserStatusList(onlineUserList)
+ renderUserStatusList(shortOnlineUserList)
+}
+
+function sortOnlineUserList (list) {
+ // sort order by isSelf, login state, idle state, alphabet name, color brightness
+ list.sort('', {
+ sortFunction: function (a, b) {
+ var usera = a.values()
+ var userb = b.values()
+ var useraIsSelf = (usera.id === window.personalInfo.id || (usera.login && usera.userid === window.personalInfo.userid))
+ var userbIsSelf = (userb.id === window.personalInfo.id || (userb.login && userb.userid === window.personalInfo.userid))
+ if (useraIsSelf && !userbIsSelf) {
+ return -1
+ } else if (!useraIsSelf && userbIsSelf) {
+ return 1
+ } else {
+ if (usera.login && !userb.login) { return -1 } else if (!usera.login && userb.login) { return 1 } else {
+ if (!usera.idle && userb.idle) { return -1 } else if (usera.idle && !userb.idle) { return 1 } else {
+ if (usera.name && userb.name && usera.name.toLowerCase() < userb.name.toLowerCase()) {
+ return -1
+ } else if (usera.name && userb.name && usera.name.toLowerCase() > userb.name.toLowerCase()) { return 1 } else { if (usera.color && userb.color && usera.color.toLowerCase() < userb.color.toLowerCase()) { return -1 } else if (usera.color && userb.color && usera.color.toLowerCase() > userb.color.toLowerCase()) { return 1 } else { return 0 } }
+ }
+ }
+ }
+ }
+ })
+}
+
+function renderUserStatusList (list) {
+ var items = list.items
+ for (var j = 0; j < items.length; j++) {
+ var item = items[j]
+ var userstatus = $(item.elm).find('.ui-user-status')
+ var usericon = $(item.elm).find('.ui-user-icon')
+ if (item.values().login && item.values().photo) {
+ usericon.css('background-image', 'url(' + item.values().photo + ')')
+ // add 1px more to right, make it feel aligned
+ usericon.css('margin-right', '6px')
+ $(item.elm).css('border-left', '4px solid ' + item.values().color)
+ usericon.css('margin-left', '-4px')
+ } else {
+ usericon.css('background-color', item.values().color)
}
- //sorting
- sortOnlineUserList(onlineUserList);
- sortOnlineUserList(shortOnlineUserList);
- //render list items
- renderUserStatusList(onlineUserList);
- renderUserStatusList(shortOnlineUserList);
-}
-
-function sortOnlineUserList(list) {
- //sort order by isSelf, login state, idle state, alphabet name, color brightness
- list.sort('', {
- sortFunction: function (a, b) {
- var usera = a.values();
- var userb = b.values();
- var useraIsSelf = (usera.id == personalInfo.id || (usera.login && usera.userid == personalInfo.userid));
- var userbIsSelf = (userb.id == personalInfo.id || (userb.login && userb.userid == personalInfo.userid));
- if (useraIsSelf && !userbIsSelf) {
- return -1;
- } else if (!useraIsSelf && userbIsSelf) {
- return 1;
- } else {
- if (usera.login && !userb.login)
- return -1;
- else if (!usera.login && userb.login)
- return 1;
- else {
- if (!usera.idle && userb.idle)
- return -1;
- else if (usera.idle && !userb.idle)
- return 1;
- else {
- if (usera.name && userb.name && usera.name.toLowerCase() < userb.name.toLowerCase()) {
- return -1;
- } else if (usera.name && userb.name && usera.name.toLowerCase() > userb.name.toLowerCase()) {
- return 1;
- } else {
- if (usera.color && userb.color && usera.color.toLowerCase() < userb.color.toLowerCase())
- return -1;
- else if (usera.color && userb.color && usera.color.toLowerCase() > userb.color.toLowerCase())
- return 1;
- else
- return 0;
- }
- }
- }
- }
- }
- });
-}
-
-function renderUserStatusList(list) {
- var items = list.items;
- for (var j = 0; j < items.length; j++) {
- var item = items[j];
- var userstatus = $(item.elm).find('.ui-user-status');
- var usericon = $(item.elm).find('.ui-user-icon');
- if (item.values().login && item.values().photo) {
- usericon.css('background-image', 'url(' + item.values().photo + ')');
- //add 1px more to right, make it feel aligned
- usericon.css('margin-right', '6px');
- $(item.elm).css('border-left', '4px solid ' + item.values().color);
- usericon.css('margin-left', '-4px');
- } else {
- usericon.css('background-color', item.values().color);
- }
- userstatus.removeClass('ui-user-status-offline ui-user-status-online ui-user-status-idle');
- if (item.values().idle)
- userstatus.addClass('ui-user-status-idle');
- else
- userstatus.addClass('ui-user-status-online');
- }
-}
-
-function deduplicateOnlineUsers(list) {
- var _onlineUsers = [];
- for (var i = 0; i < list.length; i++) {
- var user = $.extend({}, list[i]);
- if (!user.userid)
- _onlineUsers.push(user);
- else {
- var found = false;
- for (var j = 0; j < _onlineUsers.length; j++) {
- if (_onlineUsers[j].userid == user.userid) {
- //keep self color when login
- if (user.id == personalInfo.id) {
- _onlineUsers[j].color = user.color;
- }
- //keep idle state if any of self client not idle
- if (!user.idle) {
- _onlineUsers[j].idle = user.idle;
- _onlineUsers[j].color = user.color;
- }
- found = true;
- break;
- }
- }
- if (!found)
- _onlineUsers.push(user);
+ userstatus.removeClass('ui-user-status-offline ui-user-status-online ui-user-status-idle')
+ if (item.values().idle) { userstatus.addClass('ui-user-status-idle') } else { userstatus.addClass('ui-user-status-online') }
+ }
+}
+
+function deduplicateOnlineUsers (list) {
+ var _onlineUsers = []
+ for (var i = 0; i < list.length; i++) {
+ var user = $.extend({}, list[i])
+ if (!user.userid) { _onlineUsers.push(user) } else {
+ var found = false
+ for (var j = 0; j < _onlineUsers.length; j++) {
+ if (_onlineUsers[j].userid === user.userid) {
+ // keep self color when login
+ if (user.id === window.personalInfo.id) {
+ _onlineUsers[j].color = user.color
+ }
+ // keep idle state if any of self client not idle
+ if (!user.idle) {
+ _onlineUsers[j].idle = user.idle
+ _onlineUsers[j].color = user.color
+ }
+ found = true
+ break
}
+ }
+ if (!found) { _onlineUsers.push(user) }
}
- return _onlineUsers;
+ }
+ return _onlineUsers
}
-var userStatusCache = null;
+var userStatusCache = null
-function emitUserStatus(force) {
- if (!loaded) return;
- var type = null;
- if (visibleXS)
- type = 'xs';
- else if (visibleSM)
- type = 'sm';
- else if (visibleMD)
- type = 'md';
- else if (visibleLG)
- type = 'lg';
+function emitUserStatus (force) {
+ if (!window.loaded) return
+ var type = null
+ if (window.visibleXS) { type = 'xs' } else if (window.visibleSM) { type = 'sm' } else if (window.visibleMD) { type = 'md' } else if (window.visibleLG) { type = 'lg' }
- personalInfo['idle'] = idle.isAway;
- personalInfo['type'] = type;
+ window.personalInfo['idle'] = idle.isAway
+ window.personalInfo['type'] = type
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == personalInfo.id) {
- onlineUsers[i] = personalInfo;
- }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === window.personalInfo.id) {
+ window.onlineUsers[i] = window.personalInfo
}
+ }
- var userStatus = {
- idle: idle.isAway,
- type: type
- };
+ var userStatus = {
+ idle: idle.isAway,
+ type: type
+ }
- if (force || JSON.stringify(userStatus) != JSON.stringify(userStatusCache)) {
- socket.emit('user status', userStatus);
- userStatusCache = userStatus;
- }
+ if (force || JSON.stringify(userStatus) !== JSON.stringify(userStatusCache)) {
+ socket.emit('user status', userStatus)
+ userStatusCache = userStatus
+ }
}
-function checkCursorTag(coord, ele) {
- if (!ele) return; // return if element not exists
+function checkCursorTag (coord, ele) {
+ if (!ele) return // return if element not exists
// set margin
- var tagRightMargin = 0;
- var tagBottomMargin = 2;
+ var tagRightMargin = 0
+ var tagBottomMargin = 2
// use sizer to get the real doc size (won't count status bar and gutters)
- var docWidth = ui.area.codemirrorSizer.width();
- var docHeight = ui.area.codemirrorSizer.height();
+ var docWidth = ui.area.codemirrorSizer.width()
// get editor size (status bar not count in)
- var editorWidth = ui.area.codemirror.width();
- var editorHeight = ui.area.codemirror.height();
+ var editorHeight = ui.area.codemirror.height()
// get element size
- var width = ele.outerWidth();
- var height = ele.outerHeight();
- var padding = (ele.outerWidth() - ele.width()) / 2;
+ var width = ele.outerWidth()
+ var height = ele.outerHeight()
+ var padding = (ele.outerWidth() - ele.width()) / 2
// get coord position
- var left = coord.left;
- var top = coord.top;
+ var left = coord.left
+ var top = coord.top
// get doc top offset (to workaround with viewport)
- var docTopOffset = ui.area.codemirrorSizerInner.position().top;
+ var docTopOffset = ui.area.codemirrorSizerInner.position().top
// set offset
- var offsetLeft = -3;
- var offsetTop = defaultTextHeight;
+ var offsetLeft = -3
+ var offsetTop = defaultTextHeight
// only do when have width and height
- if (width > 0 && height > 0) {
+ if (width > 0 && height > 0) {
// flip x when element right bound larger than doc width
- if (left + width + offsetLeft + tagRightMargin > docWidth) {
- offsetLeft = -(width + tagRightMargin) + padding + offsetLeft;
- }
+ if (left + width + offsetLeft + tagRightMargin > docWidth) {
+ offsetLeft = -(width + tagRightMargin) + padding + offsetLeft
+ }
// flip y when element bottom bound larger than doc height
// and element top position is larger than element height
- if (top + docTopOffset + height + offsetTop + tagBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + tagBottomMargin) {
- offsetTop = -(height);
- }
+ if (top + docTopOffset + height + offsetTop + tagBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + tagBottomMargin) {
+ offsetTop = -(height)
}
+ }
// set position
- ele[0].style.left = offsetLeft + 'px';
- ele[0].style.top = offsetTop + 'px';
-}
-
-function buildCursor(user) {
- if (currentMode == modeType.view) return;
- if (!user.cursor) return;
- var coord = editor.charCoords(user.cursor, 'windows');
- coord.left = coord.left < 4 ? 4 : coord.left;
- coord.top = coord.top < 0 ? 0 : coord.top;
- var iconClass = 'fa-user';
- switch (user.type) {
- case 'xs':
- iconClass = 'fa-mobile';
- break;
- case 'sm':
- iconClass = 'fa-tablet';
- break;
- case 'md':
- iconClass = 'fa-desktop';
- break;
- case 'lg':
- iconClass = 'fa-desktop';
- break;
- }
- if ($('.CodeMirror-other-cursors').length <= 0) {
- $("<div class='CodeMirror-other-cursors'>").insertAfter('.CodeMirror-cursors');
- }
- if ($('div[data-clientid="' + user.id + '"]').length <= 0) {
- var cursor = $('<div data-clientid="' + user.id + '" class="CodeMirror-other-cursor" style="display:none;"></div>');
- cursor.attr('data-line', user.cursor.line);
- cursor.attr('data-ch', user.cursor.ch);
- cursor.attr('data-offset-left', 0);
- cursor.attr('data-offset-top', 0);
-
- var cursorbar = $('<div class="cursorbar">&nbsp;</div>');
- cursorbar[0].style.height = defaultTextHeight + 'px';
- cursorbar[0].style.borderLeft = '2px solid ' + user.color;
-
- var icon = '<i class="fa ' + iconClass + '"></i>';
-
- var cursortag = $('<div class="cursortag">' + icon + '&nbsp;<span class="name">' + user.name + '</span></div>');
- //cursortag[0].style.background = color;
- cursortag[0].style.color = user.color;
-
- cursor.attr('data-mode', 'hover');
- cursortag.delay(2000).fadeOut("fast");
- cursor.hover(
+ ele[0].style.left = offsetLeft + 'px'
+ ele[0].style.top = offsetTop + 'px'
+}
+
+function buildCursor (user) {
+ if (window.currentMode === modeType.view) return
+ if (!user.cursor) return
+ var coord = editor.charCoords(user.cursor, 'windows')
+ coord.left = coord.left < 4 ? 4 : coord.left
+ coord.top = coord.top < 0 ? 0 : coord.top
+ var iconClass = 'fa-user'
+ switch (user.type) {
+ case 'xs':
+ iconClass = 'fa-mobile'
+ break
+ case 'sm':
+ iconClass = 'fa-tablet'
+ break
+ case 'md':
+ iconClass = 'fa-desktop'
+ break
+ case 'lg':
+ iconClass = 'fa-desktop'
+ break
+ }
+ if ($('.CodeMirror-other-cursors').length <= 0) {
+ $("<div class='CodeMirror-other-cursors'>").insertAfter('.CodeMirror-cursors')
+ }
+ if ($('div[data-clientid="' + user.id + '"]').length <= 0) {
+ let cursor = $('<div data-clientid="' + user.id + '" class="CodeMirror-other-cursor" style="display:none;"></div>')
+ cursor.attr('data-line', user.cursor.line)
+ cursor.attr('data-ch', user.cursor.ch)
+ cursor.attr('data-offset-left', 0)
+ cursor.attr('data-offset-top', 0)
+
+ let cursorbar = $('<div class="cursorbar">&nbsp;</div>')
+ cursorbar[0].style.height = defaultTextHeight + 'px'
+ cursorbar[0].style.borderLeft = '2px solid ' + user.color
+
+ var icon = '<i class="fa ' + iconClass + '"></i>'
+
+ let cursortag = $('<div class="cursortag">' + icon + '&nbsp;<span class="name">' + user.name + '</span></div>')
+ // cursortag[0].style.background = color;
+ cursortag[0].style.color = user.color
+
+ cursor.attr('data-mode', 'hover')
+ cursortag.delay(2000).fadeOut('fast')
+ cursor.hover(
function () {
- if (cursor.attr('data-mode') == 'hover')
- cursortag.stop(true).fadeIn("fast");
+ if (cursor.attr('data-mode') === 'hover') { cursortag.stop(true).fadeIn('fast') }
},
function () {
- if (cursor.attr('data-mode') == 'hover')
- cursortag.stop(true).fadeOut("fast");
- });
-
- function switchMode(ele) {
- if (ele.attr('data-mode') == 'state')
- ele.attr('data-mode', 'hover');
- else if (ele.attr('data-mode') == 'hover')
- ele.attr('data-mode', 'state');
- }
-
- function switchTag(ele) {
- if (ele.css('display') === 'none')
- ele.stop(true).fadeIn("fast");
- else
- ele.stop(true).fadeOut("fast");
- }
- var hideCursorTagDelay = 2000;
- var hideCursorTagTimer = null;
-
- function hideCursorTag() {
- if (cursor.attr('data-mode') == 'hover')
- cursortag.fadeOut("fast");
- }
- cursor.on('touchstart', function (e) {
- var display = cursortag.css('display');
- cursortag.stop(true).fadeIn("fast");
- clearTimeout(hideCursorTagTimer);
- hideCursorTagTimer = setTimeout(hideCursorTag, hideCursorTagDelay);
- if (display === 'none') {
- e.preventDefault();
- e.stopPropagation();
- }
- });
- cursortag.on('mousedown touchstart', function (e) {
- if (cursor.attr('data-mode') == 'state')
- switchTag(cursortag);
- switchMode(cursor);
- e.preventDefault();
- e.stopPropagation();
- });
-
- cursor.append(cursorbar);
- cursor.append(cursortag);
-
- cursor[0].style.left = coord.left + 'px';
- cursor[0].style.top = coord.top + 'px';
- $('.CodeMirror-other-cursors').append(cursor);
-
- if (!user.idle)
- cursor.stop(true).fadeIn();
-
- checkCursorTag(coord, cursortag);
- } else {
- var cursor = $('div[data-clientid="' + user.id + '"]');
- var lineDiff = Math.abs(cursor.attr('data-line') - user.cursor.line);
- cursor.attr('data-line', user.cursor.line);
- cursor.attr('data-ch', user.cursor.ch);
-
- var cursorbar = cursor.find('.cursorbar');
- cursorbar[0].style.height = defaultTextHeight + 'px';
- cursorbar[0].style.borderLeft = '2px solid ' + user.color;
-
- var cursortag = cursor.find('.cursortag');
- cursortag.find('i').removeClass().addClass('fa').addClass(iconClass);
- cursortag.find(".name").text(user.name);
-
- if (cursor.css('display') === 'none') {
- cursor[0].style.left = coord.left + 'px';
- cursor[0].style.top = coord.top + 'px';
- } else {
- cursor.animate({
- "left": coord.left,
- "top": coord.top
- }, {
- duration: cursorAnimatePeriod,
- queue: false
- });
- }
+ if (cursor.attr('data-mode') === 'hover') { cursortag.stop(true).fadeOut('fast') }
+ })
- if (user.idle && cursor.css('display') !== 'none')
- cursor.stop(true).fadeOut();
- else if (!user.idle && cursor.css('display') === 'none')
- cursor.stop(true).fadeIn();
+ var hideCursorTagDelay = 2000
+ var hideCursorTagTimer = null
- checkCursorTag(coord, cursortag);
+ var switchMode = function (ele) {
+ if (ele.attr('data-mode') === 'state') { ele.attr('data-mode', 'hover') } else if (ele.attr('data-mode') === 'hover') { ele.attr('data-mode', 'state') }
}
-}
-//editor actions
-function removeNullByte(cm, change) {
- var str = change.text.join("\n");
- if (/\u0000/g.test(str) && change.update) {
- change.update(change.from, change.to, str.replace(/\u0000/g, "").split("\n"));
+ var switchTag = function (ele) {
+ if (ele.css('display') === 'none') { ele.stop(true).fadeIn('fast') } else { ele.stop(true).fadeOut('fast') }
}
-}
-function enforceMaxLength(cm, change) {
- var maxLength = cm.getOption("maxLength");
- if (maxLength && change.update) {
- var str = change.text.join("\n");
- var delta = str.length - (cm.indexFromPos(change.to) - cm.indexFromPos(change.from));
- if (delta <= 0) {
- return false;
- }
- delta = cm.getValue().length + delta - maxLength;
- if (delta > 0) {
- str = str.substr(0, str.length - delta);
- change.update(change.from, change.to, str.split("\n"));
- return true;
- }
+
+ var hideCursorTag = function () {
+ if (cursor.attr('data-mode') === 'hover') { cursortag.fadeOut('fast') }
}
- return false;
-}
-var ignoreEmitEvents = ['setValue', 'ignoreHistory'];
-editor.on('beforeChange', function (cm, change) {
- if (debug)
- console.debug(change);
- removeNullByte(cm, change);
- if (enforceMaxLength(cm, change)) {
- $('.limit-modal').modal('show');
- }
- var isIgnoreEmitEvent = (ignoreEmitEvents.indexOf(change.origin) != -1);
- if (!isIgnoreEmitEvent) {
- if (!havePermission()) {
- change.canceled = true;
- switch (permission) {
- case "editable":
- $('.signin-modal').modal('show');
- break;
- case "locked":
- case "private":
- $('.locked-modal').modal('show');
- break;
- }
- }
+ cursor.on('touchstart', function (e) {
+ var display = cursortag.css('display')
+ cursortag.stop(true).fadeIn('fast')
+ clearTimeout(hideCursorTagTimer)
+ hideCursorTagTimer = setTimeout(hideCursorTag, hideCursorTagDelay)
+ if (display === 'none') {
+ e.preventDefault()
+ e.stopPropagation()
+ }
+ })
+ cursortag.on('mousedown touchstart', function (e) {
+ if (cursor.attr('data-mode') === 'state') { switchTag(cursortag) }
+ switchMode(cursor)
+ e.preventDefault()
+ e.stopPropagation()
+ })
+
+ cursor.append(cursorbar)
+ cursor.append(cursortag)
+
+ cursor[0].style.left = coord.left + 'px'
+ cursor[0].style.top = coord.top + 'px'
+ $('.CodeMirror-other-cursors').append(cursor)
+
+ if (!user.idle) { cursor.stop(true).fadeIn() }
+
+ checkCursorTag(coord, cursortag)
+ } else {
+ let cursor = $('div[data-clientid="' + user.id + '"]')
+ cursor.attr('data-line', user.cursor.line)
+ cursor.attr('data-ch', user.cursor.ch)
+
+ let cursorbar = cursor.find('.cursorbar')
+ cursorbar[0].style.height = defaultTextHeight + 'px'
+ cursorbar[0].style.borderLeft = '2px solid ' + user.color
+
+ let cursortag = cursor.find('.cursortag')
+ cursortag.find('i').removeClass().addClass('fa').addClass(iconClass)
+ cursortag.find('.name').text(user.name)
+
+ if (cursor.css('display') === 'none') {
+ cursor[0].style.left = coord.left + 'px'
+ cursor[0].style.top = coord.top + 'px'
} else {
- if (change.origin == 'ignoreHistory') {
- setHaveUnreadChanges(true);
- updateTitleReminder();
- }
- }
- if (cmClient && !socket.connected)
- cmClient.editorAdapter.ignoreNextChange = true;
-});
+ cursor.animate({
+ 'left': coord.left,
+ 'top': coord.top
+ }, {
+ duration: cursorAnimatePeriod,
+ queue: false
+ })
+ }
+
+ if (user.idle && cursor.css('display') !== 'none') { cursor.stop(true).fadeOut() } else if (!user.idle && cursor.css('display') === 'none') { cursor.stop(true).fadeIn() }
+
+ checkCursorTag(coord, cursortag)
+ }
+}
+
+// editor actions
+function removeNullByte (cm, change) {
+ var str = change.text.join('\n')
+ if (/\u0000/g.test(str) && change.update) {
+ change.update(change.from, change.to, str.replace(/\u0000/g, '').split('\n'))
+ }
+}
+function enforceMaxLength (cm, change) {
+ var maxLength = cm.getOption('maxLength')
+ if (maxLength && change.update) {
+ var str = change.text.join('\n')
+ var delta = str.length - (cm.indexFromPos(change.to) - cm.indexFromPos(change.from))
+ if (delta <= 0) {
+ return false
+ }
+ delta = cm.getValue().length + delta - maxLength
+ if (delta > 0) {
+ str = str.substr(0, str.length - delta)
+ change.update(change.from, change.to, str.split('\n'))
+ return true
+ }
+ }
+ return false
+}
+var ignoreEmitEvents = ['setValue', 'ignoreHistory']
+editor.on('beforeChange', function (cm, change) {
+ if (debug) { console.debug(change) }
+ removeNullByte(cm, change)
+ if (enforceMaxLength(cm, change)) {
+ $('.limit-modal').modal('show')
+ }
+ var isIgnoreEmitEvent = (ignoreEmitEvents.indexOf(change.origin) !== -1)
+ if (!isIgnoreEmitEvent) {
+ if (!havePermission()) {
+ change.canceled = true
+ switch (permission) {
+ case 'editable':
+ $('.signin-modal').modal('show')
+ break
+ case 'locked':
+ case 'private':
+ $('.locked-modal').modal('show')
+ break
+ }
+ }
+ } else {
+ if (change.origin === 'ignoreHistory') {
+ setHaveUnreadChanges(true)
+ updateTitleReminder()
+ }
+ }
+ if (cmClient && !socket.connected) { cmClient.editorAdapter.ignoreNextChange = true }
+})
editor.on('cut', function () {
- //na
-});
+ // na
+})
editor.on('paste', function () {
- //na
-});
+ // na
+})
editor.on('changes', function (cm, changes) {
- updateHistory();
- var docLength = editor.getValue().length;
- //workaround for big documents
- var newViewportMargin = 20;
- if (docLength > 20000) {
- newViewportMargin = 1;
- } else if (docLength > 10000) {
- newViewportMargin = 10;
- } else if (docLength > 5000) {
- newViewportMargin = 15;
- }
- if (newViewportMargin != viewportMargin) {
- viewportMargin = newViewportMargin;
- windowResize();
- }
- checkEditorScrollbar();
- if (ui.area.codemirrorScroll[0].scrollHeight > ui.area.view[0].scrollHeight && editorHasFocus()) {
- postUpdateEvent = function () {
- syncScrollToView();
- postUpdateEvent = null;
- };
- }
-});
+ updateHistory()
+ var docLength = editor.getValue().length
+ // workaround for big documents
+ var newViewportMargin = 20
+ if (docLength > 20000) {
+ newViewportMargin = 1
+ } else if (docLength > 10000) {
+ newViewportMargin = 10
+ } else if (docLength > 5000) {
+ newViewportMargin = 15
+ }
+ if (newViewportMargin !== viewportMargin) {
+ viewportMargin = newViewportMargin
+ windowResize()
+ }
+ checkEditorScrollbar()
+ if (ui.area.codemirrorScroll[0].scrollHeight > ui.area.view[0].scrollHeight && editorHasFocus()) {
+ postUpdateEvent = function () {
+ syncScrollToView()
+ postUpdateEvent = null
+ }
+ }
+})
editor.on('focus', function (cm) {
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == personalInfo.id) {
- onlineUsers[i].cursor = editor.getCursor();
- }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === window.personalInfo.id) {
+ window.onlineUsers[i].cursor = editor.getCursor()
}
- personalInfo['cursor'] = editor.getCursor();
- socket.emit('cursor focus', editor.getCursor());
-});
+ }
+ window.personalInfo['cursor'] = editor.getCursor()
+ socket.emit('cursor focus', editor.getCursor())
+})
editor.on('cursorActivity', function (cm) {
- updateStatusBar();
- cursorActivity();
-});
+ updateStatusBar()
+ cursorActivity()
+})
editor.on('beforeSelectionChange', function (doc, selections) {
- if (selections)
- selection = selections.ranges[0];
- else
- selection = null;
- updateStatusBar();
-});
-
-var cursorActivity = _.debounce(cursorActivityInner, cursorActivityDebounce);
-
-function cursorActivityInner() {
- if (editorHasFocus() && !Visibility.hidden()) {
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == personalInfo.id) {
- onlineUsers[i].cursor = editor.getCursor();
- }
- }
- personalInfo['cursor'] = editor.getCursor();
- socket.emit('cursor activity', editor.getCursor());
+ if (selections) { selection = selections.ranges[0] } else { selection = null }
+ updateStatusBar()
+})
+
+var cursorActivity = _.debounce(cursorActivityInner, cursorActivityDebounce)
+
+function cursorActivityInner () {
+ if (editorHasFocus() && !Visibility.hidden()) {
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === window.personalInfo.id) {
+ window.onlineUsers[i].cursor = editor.getCursor()
+ }
}
+ window.personalInfo['cursor'] = editor.getCursor()
+ socket.emit('cursor activity', editor.getCursor())
+ }
}
editor.on('blur', function (cm) {
- for (var i = 0; i < onlineUsers.length; i++) {
- if (onlineUsers[i].id == personalInfo.id) {
- onlineUsers[i].cursor = null;
- }
+ for (var i = 0; i < window.onlineUsers.length; i++) {
+ if (window.onlineUsers[i].id === window.personalInfo.id) {
+ window.onlineUsers[i].cursor = null
}
- personalInfo['cursor'] = null;
- socket.emit('cursor blur');
-});
-
-function saveInfo() {
- var scrollbarStyle = editor.getOption('scrollbarStyle');
- var left = $(window).scrollLeft();
- var top = $(window).scrollTop();
- switch (currentMode) {
- case modeType.edit:
- if (scrollbarStyle == 'native') {
- lastInfo.edit.scroll.left = left;
- lastInfo.edit.scroll.top = top;
- } else {
- lastInfo.edit.scroll = editor.getScrollInfo();
- }
- break;
- case modeType.view:
- lastInfo.view.scroll.left = left;
- lastInfo.view.scroll.top = top;
- break;
- case modeType.both:
- lastInfo.edit.scroll = editor.getScrollInfo();
- lastInfo.view.scroll.left = ui.area.view.scrollLeft();
- lastInfo.view.scroll.top = ui.area.view.scrollTop();
- break;
- }
- lastInfo.edit.cursor = editor.getCursor();
- lastInfo.edit.selections = editor.listSelections();
- lastInfo.needRestore = true;
-}
-
-function restoreInfo() {
- var scrollbarStyle = editor.getOption('scrollbarStyle');
- if (lastInfo.needRestore) {
- var line = lastInfo.edit.cursor.line;
- var ch = lastInfo.edit.cursor.ch;
- editor.setCursor(line, ch);
- editor.setSelections(lastInfo.edit.selections);
- switch (currentMode) {
- case modeType.edit:
- if (scrollbarStyle == 'native') {
- $(window).scrollLeft(lastInfo.edit.scroll.left);
- $(window).scrollTop(lastInfo.edit.scroll.top);
- } else {
- var left = lastInfo.edit.scroll.left;
- var top = lastInfo.edit.scroll.top;
- editor.scrollIntoView();
- editor.scrollTo(left, top);
- }
- break;
- case modeType.view:
- $(window).scrollLeft(lastInfo.view.scroll.left);
- $(window).scrollTop(lastInfo.view.scroll.top);
- break;
- case modeType.both:
- var left = lastInfo.edit.scroll.left;
- var top = lastInfo.edit.scroll.top;
- editor.scrollIntoView();
- editor.scrollTo(left, top);
- ui.area.view.scrollLeft(lastInfo.view.scroll.left);
- ui.area.view.scrollTop(lastInfo.view.scroll.top);
- break;
- }
+ }
+ window.personalInfo['cursor'] = null
+ socket.emit('cursor blur')
+})
- lastInfo.needRestore = false;
+function saveInfo () {
+ var scrollbarStyle = editor.getOption('scrollbarStyle')
+ var left = $(window).scrollLeft()
+ var top = $(window).scrollTop()
+ switch (window.currentMode) {
+ case modeType.edit:
+ if (scrollbarStyle === 'native') {
+ window.lastInfo.edit.scroll.left = left
+ window.lastInfo.edit.scroll.top = top
+ } else {
+ window.lastInfo.edit.scroll = editor.getScrollInfo()
+ }
+ break
+ case modeType.view:
+ window.lastInfo.view.scroll.left = left
+ window.lastInfo.view.scroll.top = top
+ break
+ case modeType.both:
+ window.lastInfo.edit.scroll = editor.getScrollInfo()
+ window.lastInfo.view.scroll.left = ui.area.view.scrollLeft()
+ window.lastInfo.view.scroll.top = ui.area.view.scrollTop()
+ break
+ }
+ window.lastInfo.edit.cursor = editor.getCursor()
+ window.lastInfo.edit.selections = editor.listSelections()
+ window.lastInfo.needRestore = true
+}
+
+function restoreInfo () {
+ var scrollbarStyle = editor.getOption('scrollbarStyle')
+ if (window.lastInfo.needRestore) {
+ var line = window.lastInfo.edit.cursor.line
+ var ch = window.lastInfo.edit.cursor.ch
+ editor.setCursor(line, ch)
+ editor.setSelections(window.lastInfo.edit.selections)
+ switch (window.currentMode) {
+ case modeType.edit:
+ if (scrollbarStyle === 'native') {
+ $(window).scrollLeft(window.lastInfo.edit.scroll.left)
+ $(window).scrollTop(window.lastInfo.edit.scroll.top)
+ } else {
+ let left = window.lastInfo.edit.scroll.left
+ let top = window.lastInfo.edit.scroll.top
+ editor.scrollIntoView()
+ editor.scrollTo(left, top)
+ }
+ break
+ case modeType.view:
+ $(window).scrollLeft(window.lastInfo.view.scroll.left)
+ $(window).scrollTop(window.lastInfo.view.scroll.top)
+ break
+ case modeType.both:
+ let left = window.lastInfo.edit.scroll.left
+ let top = window.lastInfo.edit.scroll.top
+ editor.scrollIntoView()
+ editor.scrollTo(left, top)
+ ui.area.view.scrollLeft(window.lastInfo.view.scroll.left)
+ ui.area.view.scrollTop(window.lastInfo.view.scroll.top)
+ break
}
+
+ window.lastInfo.needRestore = false
+ }
}
-//view actions
-function refreshView() {
- ui.area.markdown.html('');
- isDirty = true;
- updateViewInner();
+// view actions
+function refreshView () {
+ ui.area.markdown.html('')
+ window.isDirty = true
+ updateViewInner()
}
var updateView = _.debounce(function () {
- editor.operation(updateViewInner);
-}, updateViewDebounce);
-
-var lastResult = null;
-var postUpdateEvent = null;
-
-function updateViewInner() {
- if (currentMode == modeType.edit || !isDirty) return;
- var value = editor.getValue();
- var lastMeta = md.meta;
- md.meta = {};
- delete md.metaError;
- var rendered = md.render(value);
- if (md.meta.type && md.meta.type === 'slide') {
- var slideOptions = {
- separator: '^(\r\n?|\n)---(\r\n?|\n)$',
- verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
- };
- var slides = RevealMarkdown.slidify(editor.getValue(), slideOptions);
- ui.area.markdown.html(slides);
- RevealMarkdown.initialize();
+ editor.operation(updateViewInner)
+}, updateViewDebounce)
+
+var lastResult = null
+var postUpdateEvent = null
+
+function updateViewInner () {
+ if (window.currentMode === modeType.edit || !window.isDirty) return
+ var value = editor.getValue()
+ var lastMeta = md.meta
+ md.meta = {}
+ delete md.metaError
+ var rendered = md.render(value)
+ if (md.meta.type && md.meta.type === 'slide') {
+ var slideOptions = {
+ separator: '^(\r\n?|\n)---(\r\n?|\n)$',
+ verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
+ }
+ var slides = window.RevealMarkdown.slidify(editor.getValue(), slideOptions)
+ ui.area.markdown.html(slides)
+ window.RevealMarkdown.initialize()
// prevent XSS
- ui.area.markdown.html(preventXSS(ui.area.markdown.html()));
- ui.area.markdown.addClass('slides');
- syncscroll = false;
- checkSyncToggle();
- } else {
- if (lastMeta.type && lastMeta.type === 'slide') {
- refreshView();
- ui.area.markdown.removeClass('slides');
- syncscroll = true;
- checkSyncToggle();
- }
+ ui.area.markdown.html(preventXSS(ui.area.markdown.html()))
+ ui.area.markdown.addClass('slides')
+ window.syncscroll = false
+ checkSyncToggle()
+ } else {
+ if (lastMeta.type && lastMeta.type === 'slide') {
+ refreshView()
+ ui.area.markdown.removeClass('slides')
+ window.syncscroll = true
+ checkSyncToggle()
+ }
// only render again when meta changed
- if (JSON.stringify(md.meta) != JSON.stringify(lastMeta)) {
- parseMeta(md, ui.area.codemirror, ui.area.markdown, $('#ui-toc'), $('#ui-toc-affix'));
- rendered = md.render(value);
- }
+ if (JSON.stringify(md.meta) !== JSON.stringify(lastMeta)) {
+ parseMeta(md, ui.area.codemirror, ui.area.markdown, $('#ui-toc'), $('#ui-toc-affix'))
+ rendered = md.render(value)
+ }
// prevent XSS
- rendered = preventXSS(rendered);
- var result = postProcess(rendered).children().toArray();
- partialUpdate(result, lastResult, ui.area.markdown.children().toArray());
- if (result && lastResult && result.length != lastResult.length)
- updateDataAttrs(result, ui.area.markdown.children().toArray());
- lastResult = $(result).clone();
- }
- finishView(ui.area.markdown);
- autoLinkify(ui.area.markdown);
- deduplicatedHeaderId(ui.area.markdown);
- renderTOC(ui.area.markdown);
- generateToc('ui-toc');
- generateToc('ui-toc-affix');
- generateScrollspy();
- updateScrollspy();
- smoothHashScroll();
- isDirty = false;
- clearMap();
- //buildMap();
- updateTitleReminder();
- if (postUpdateEvent && typeof postUpdateEvent === 'function')
- postUpdateEvent();
-}
-
-var updateHistoryDebounce = 600;
+ rendered = preventXSS(rendered)
+ var result = postProcess(rendered).children().toArray()
+ partialUpdate(result, lastResult, ui.area.markdown.children().toArray())
+ if (result && lastResult && result.length !== lastResult.length) { updateDataAttrs(result, ui.area.markdown.children().toArray()) }
+ lastResult = $(result).clone()
+ }
+ finishView(ui.area.markdown)
+ autoLinkify(ui.area.markdown)
+ deduplicatedHeaderId(ui.area.markdown)
+ renderTOC(ui.area.markdown)
+ generateToc('ui-toc')
+ generateToc('ui-toc-affix')
+ generateScrollspy()
+ updateScrollspy()
+ smoothHashScroll()
+ window.isDirty = false
+ clearMap()
+ // buildMap();
+ updateTitleReminder()
+ if (postUpdateEvent && typeof postUpdateEvent === 'function') { postUpdateEvent() }
+}
+
+var updateHistoryDebounce = 600
var updateHistory = _.debounce(updateHistoryInner, updateHistoryDebounce)
-function updateHistoryInner() {
- writeHistory(renderFilename(ui.area.markdown), renderTags(ui.area.markdown));
-}
-
-function updateDataAttrs(src, des) {
- //sync data attr startline and endline
- for (var i = 0; i < src.length; i++) {
- copyAttribute(src[i], des[i], 'data-startline');
- copyAttribute(src[i], des[i], 'data-endline');
- }
-}
-
-function partialUpdate(src, tar, des) {
- if (!src || src.length == 0 || !tar || tar.length == 0 || !des || des.length == 0) {
- ui.area.markdown.html(src);
- return;
- }
- if (src.length == tar.length) { //same length
- for (var i = 0; i < src.length; i++) {
- copyAttribute(src[i], des[i], 'data-startline');
- copyAttribute(src[i], des[i], 'data-endline');
- var rawSrc = cloneAndRemoveDataAttr(src[i]);
- var rawTar = cloneAndRemoveDataAttr(tar[i]);
- if (rawSrc.outerHTML != rawTar.outerHTML) {
- //console.log(rawSrc);
- //console.log(rawTar);
- $(des[i]).replaceWith(src[i]);
- }
- }
- } else { //diff length
- var start = 0;
- var end = 0;
- //find diff start position
- for (var i = 0; i < tar.length; i++) {
- //copyAttribute(src[i], des[i], 'data-startline');
- //copyAttribute(src[i], des[i], 'data-endline');
- var rawSrc = cloneAndRemoveDataAttr(src[i]);
- var rawTar = cloneAndRemoveDataAttr(tar[i]);
- if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
- start = i;
- break;
- }
- }
- //find diff end position
- var srcEnd = 0;
- var tarEnd = 0;
- for (var i = 0; i < src.length; i++) {
- //copyAttribute(src[i], des[i], 'data-startline');
- //copyAttribute(src[i], des[i], 'data-endline');
- var rawSrc = cloneAndRemoveDataAttr(src[i]);
- var rawTar = cloneAndRemoveDataAttr(tar[i]);
- if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
- start = i;
- break;
- }
- }
- //tar end
- 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');
- //copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline');
- var rawSrc = cloneAndRemoveDataAttr(src[srcLength - i]);
- var rawTar = cloneAndRemoveDataAttr(tar[tarLength - i]);
- if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
- tarEnd = tar.length - i;
- break;
- }
- }
- //src end
- 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');
- //copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline');
- var rawSrc = cloneAndRemoveDataAttr(src[srcLength - i]);
- var rawTar = cloneAndRemoveDataAttr(tar[tarLength - i]);
- if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
- srcEnd = src.length - i;
- break;
- }
- }
- //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)
- overlap++;
- else
- break;
- }
- if (debug)
- console.log('overlap:' + overlap);
- //show diff content
- if (debug) {
- console.log('start:' + start);
- console.log('tarEnd:' + tarEnd);
- console.log('srcEnd:' + srcEnd);
- }
- tarEnd += overlap;
- srcEnd += overlap;
- 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);
- }
- }
- //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]);
- }
- }
- //add elements
- if (debug) {
- console.log('ADD ELEMENTS');
- console.log(newElements.join('\n'));
- }
- 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;
- var rawEl = $(el).clone();
- rawEl.removeAttr('data-startline data-endline');
- rawEl.find('[data-startline]').removeAttr('data-startline data-endline');
- return rawEl[0];
-}
-
-function copyAttribute(src, des, attr) {
- if (src && src.getAttribute(attr) && des)
- des.setAttribute(attr, src.getAttribute(attr));
+function updateHistoryInner () {
+ writeHistory(renderFilename(ui.area.markdown), renderTags(ui.area.markdown))
+}
+
+function updateDataAttrs (src, des) {
+ // sync data attr startline and endline
+ for (var i = 0; i < src.length; i++) {
+ copyAttribute(src[i], des[i], 'data-startline')
+ copyAttribute(src[i], des[i], 'data-endline')
+ }
+}
+
+function partialUpdate (src, tar, des) {
+ if (!src || src.length === 0 || !tar || tar.length === 0 || !des || des.length === 0) {
+ ui.area.markdown.html(src)
+ return
+ }
+ if (src.length === tar.length) { // same length
+ for (let i = 0; i < src.length; i++) {
+ copyAttribute(src[i], des[i], 'data-startline')
+ copyAttribute(src[i], des[i], 'data-endline')
+ var rawSrc = cloneAndRemoveDataAttr(src[i])
+ var rawTar = cloneAndRemoveDataAttr(tar[i])
+ if (rawSrc.outerHTML !== rawTar.outerHTML) {
+ // console.log(rawSrc);
+ // console.log(rawTar);
+ $(des[i]).replaceWith(src[i])
+ }
+ }
+ } else { // diff length
+ var start = 0
+ // find diff start position
+ for (let i = 0; i < tar.length; i++) {
+ // copyAttribute(src[i], des[i], 'data-startline');
+ // copyAttribute(src[i], des[i], 'data-endline');
+ let rawSrc = cloneAndRemoveDataAttr(src[i])
+ let rawTar = cloneAndRemoveDataAttr(tar[i])
+ if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
+ start = i
+ break
+ }
+ }
+ // find diff end position
+ var srcEnd = 0
+ var tarEnd = 0
+ for (let i = 0; i < src.length; i++) {
+ // copyAttribute(src[i], des[i], 'data-startline');
+ // copyAttribute(src[i], des[i], 'data-endline');
+ let rawSrc = cloneAndRemoveDataAttr(src[i])
+ let rawTar = cloneAndRemoveDataAttr(tar[i])
+ if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
+ start = i
+ break
+ }
+ }
+ // tar end
+ for (let i = 1; i <= tar.length + 1; i++) {
+ let srcLength = src.length
+ let tarLength = tar.length
+ // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline');
+ // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline');
+ let rawSrc = cloneAndRemoveDataAttr(src[srcLength - i])
+ let rawTar = cloneAndRemoveDataAttr(tar[tarLength - i])
+ if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
+ tarEnd = tar.length - i
+ break
+ }
+ }
+ // src end
+ for (let i = 1; i <= src.length + 1; i++) {
+ let srcLength = src.length
+ let tarLength = tar.length
+ // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline');
+ // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline');
+ let rawSrc = cloneAndRemoveDataAttr(src[srcLength - i])
+ let rawTar = cloneAndRemoveDataAttr(tar[tarLength - i])
+ if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
+ srcEnd = src.length - i
+ break
+ }
+ }
+ // 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) { overlap++ } else { break }
+ }
+ if (debug) { console.log('overlap:' + overlap) }
+ // show diff content
+ if (debug) {
+ console.log('start:' + start)
+ console.log('tarEnd:' + tarEnd)
+ console.log('srcEnd:' + srcEnd)
+ }
+ tarEnd += overlap
+ srcEnd += overlap
+ var repeatAdd = (start - srcEnd) < (start - tarEnd)
+ var repeatDiff = Math.abs(srcEnd - tarEnd) - 1
+ // push new elements
+ var newElements = []
+ if (srcEnd >= start) {
+ for (let j = start; j <= srcEnd; j++) {
+ if (!src[j]) continue
+ newElements.push(src[j].outerHTML)
+ }
+ } else if (repeatAdd) {
+ for (let j = srcEnd - repeatDiff; j <= srcEnd; j++) {
+ if (!des[j]) continue
+ newElements.push(des[j].outerHTML)
+ }
+ }
+ // push remove elements
+ var removeElements = []
+ if (tarEnd >= start) {
+ for (let j = start; j <= tarEnd; j++) {
+ if (!des[j]) continue
+ removeElements.push(des[j])
+ }
+ } else if (!repeatAdd) {
+ for (let j = start; j <= start + repeatDiff; j++) {
+ if (!des[j]) continue
+ removeElements.push(des[j])
+ }
+ }
+ // add elements
+ if (debug) {
+ console.log('ADD ELEMENTS')
+ console.log(newElements.join('\n'))
+ }
+ if (des[start]) { $(newElements.join('')).insertBefore(des[start]) } else { $(newElements.join('')).insertAfter(des[start - 1]) }
+ // remove elements
+ if (debug) { console.log('REMOVE ELEMENTS') }
+ for (let 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
+ var rawEl = $(el).clone()
+ rawEl.removeAttr('data-startline data-endline')
+ rawEl.find('[data-startline]').removeAttr('data-startline data-endline')
+ return rawEl[0]
+}
+
+function copyAttribute (src, des, attr) {
+ if (src && src.getAttribute(attr) && des) { des.setAttribute(attr, src.getAttribute(attr)) }
}
if ($('.cursor-menu').length <= 0) {
- $("<div class='cursor-menu'>").insertAfter('.CodeMirror-cursors');
+ $("<div class='cursor-menu'>").insertAfter('.CodeMirror-cursors')
}
-function reverseSortCursorMenu(dropdown) {
- var items = dropdown.find('.textcomplete-item');
- items.sort(function (a, b) {
- return $(b).attr('data-index') - $(a).attr('data-index');
- });
- return items;
+function reverseSortCursorMenu (dropdown) {
+ var items = dropdown.find('.textcomplete-item')
+ items.sort(function (a, b) {
+ return $(b).attr('data-index') - $(a).attr('data-index')
+ })
+ return items
}
-var checkCursorMenu = _.throttle(checkCursorMenuInner, cursorMenuThrottle);
+var checkCursorMenu = _.throttle(checkCursorMenuInner, cursorMenuThrottle)
-function checkCursorMenuInner() {
+function checkCursorMenuInner () {
// get element
- var dropdown = $('.cursor-menu > .dropdown-menu');
+ var dropdown = $('.cursor-menu > .dropdown-menu')
// return if not exists
- if (dropdown.length <= 0) return;
+ if (dropdown.length <= 0) return
// set margin
- var menuRightMargin = 10;
- var menuBottomMargin = 4;
+ var menuRightMargin = 10
+ var menuBottomMargin = 4
// use sizer to get the real doc size (won't count status bar and gutters)
- var docWidth = ui.area.codemirrorSizer.width();
- var docHeight = ui.area.codemirrorSizer.height();
+ var docWidth = ui.area.codemirrorSizer.width()
// get editor size (status bar not count in)
- var editorWidth = ui.area.codemirror.width();
- var editorHeight = ui.area.codemirror.height();
+ var editorHeight = ui.area.codemirror.height()
// get element size
- var width = dropdown.outerWidth();
- var height = dropdown.outerHeight();
+ var width = dropdown.outerWidth()
+ var height = dropdown.outerHeight()
// get cursor
- var cursor = editor.getCursor();
+ var cursor = editor.getCursor()
// set element cursor data
- if (!dropdown.hasClass('CodeMirror-other-cursor'))
- dropdown.addClass('CodeMirror-other-cursor');
- dropdown.attr('data-line', cursor.line);
- dropdown.attr('data-ch', cursor.ch);
+ if (!dropdown.hasClass('CodeMirror-other-cursor')) { dropdown.addClass('CodeMirror-other-cursor') }
+ dropdown.attr('data-line', cursor.line)
+ dropdown.attr('data-ch', cursor.ch)
// get coord position
- var coord = editor.charCoords({
- line: cursor.line,
- ch: cursor.ch
- }, 'windows');
- var left = coord.left;
- var top = coord.top;
+ var coord = editor.charCoords({
+ line: cursor.line,
+ ch: cursor.ch
+ }, 'windows')
+ var left = coord.left
+ var top = coord.top
// get doc top offset (to workaround with viewport)
- var docTopOffset = ui.area.codemirrorSizerInner.position().top;
+ var docTopOffset = ui.area.codemirrorSizerInner.position().top
// set offset
- var offsetLeft = 0;
- var offsetTop = defaultTextHeight;
+ var offsetLeft = 0
+ var offsetTop = defaultTextHeight
// set up side down
- window.upSideDown = false;
- var lastUpSideDown = upSideDown = false;
+ window.upSideDown = false
+ var lastUpSideDown = window.upSideDown = false
// only do when have width and height
- if (width > 0 && height > 0) {
+ if (width > 0 && height > 0) {
// make element right bound not larger than doc width
- if (left + width + offsetLeft + menuRightMargin > docWidth)
- offsetLeft = -(left + width - docWidth + menuRightMargin);
+ if (left + width + offsetLeft + menuRightMargin > docWidth) { offsetLeft = -(left + width - docWidth + menuRightMargin) }
// flip y when element bottom bound larger than doc height
// and element top position is larger than element height
- if (top + docTopOffset + height + offsetTop + menuBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + menuBottomMargin) {
- offsetTop = -(height + menuBottomMargin);
+ if (top + docTopOffset + height + offsetTop + menuBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + menuBottomMargin) {
+ offsetTop = -(height + menuBottomMargin)
// reverse sort menu because upSideDown
- dropdown.html(reverseSortCursorMenu(dropdown));
- upSideDown = true;
- }
- var textCompleteDropdown = $(editor.getInputField()).data('textComplete').dropdown;
- lastUpSideDown = textCompleteDropdown.upSideDown;
- textCompleteDropdown.upSideDown = upSideDown;
+ dropdown.html(reverseSortCursorMenu(dropdown))
+ window.upSideDown = true
}
+ var textCompleteDropdown = $(editor.getInputField()).data('textComplete').dropdown
+ lastUpSideDown = textCompleteDropdown.upSideDown
+ textCompleteDropdown.upSideDown = window.upSideDown
+ }
// make menu scroll top only if upSideDown changed
- if (upSideDown !== lastUpSideDown)
- dropdown.scrollTop(dropdown[0].scrollHeight);
+ if (window.upSideDown !== lastUpSideDown) { dropdown.scrollTop(dropdown[0].scrollHeight) }
// set element offset data
- dropdown.attr('data-offset-left', offsetLeft);
- dropdown.attr('data-offset-top', offsetTop);
+ dropdown.attr('data-offset-left', offsetLeft)
+ dropdown.attr('data-offset-top', offsetTop)
// set position
- dropdown[0].style.left = left + offsetLeft + 'px';
- dropdown[0].style.top = top + offsetTop + 'px';
+ dropdown[0].style.left = left + offsetLeft + 'px'
+ dropdown[0].style.top = top + offsetTop + 'px'
}
-function checkInIndentCode() {
+function checkInIndentCode () {
// if line starts with tab or four spaces is a code block
- var line = editor.getLine(editor.getCursor().line);
- var isIndentCode = ((line.substr(0, 4) === ' ') || (line.substr(0, 1) === '\t'));
- return isIndentCode;
-}
-
-var isInCode = false;
-
-function checkInCode() {
- isInCode = checkAbove(matchInCode) || checkInIndentCode();
-}
-
-function checkAbove(method) {
- var cursor = editor.getCursor();
- var text = [];
- for (var i = 0; i < cursor.line; i++) //contain current line
- text.push(editor.getLine(i));
- text = text.join('\n') + '\n' + editor.getLine(cursor.line).slice(0, cursor.ch);
- //console.log(text);
- return method(text);
-}
-
-function checkBelow(method) {
- var cursor = editor.getCursor();
- var count = editor.lineCount();
- var text = [];
- for (var i = cursor.line + 1; i < count; i++) //contain current line
- text.push(editor.getLine(i));
- text = editor.getLine(cursor.line).slice(cursor.ch) + '\n' + text.join('\n');
- //console.log(text);
- return method(text);
-}
-
-function matchInCode(text) {
- var match;
- match = text.match(/`{3,}/g);
+ var line = editor.getLine(editor.getCursor().line)
+ var isIndentCode = ((line.substr(0, 4) === ' ') || (line.substr(0, 1) === '\t'))
+ return isIndentCode
+}
+
+var isInCode = false
+
+function checkInCode () {
+ isInCode = checkAbove(matchInCode) || checkInIndentCode()
+}
+
+function checkAbove (method) {
+ var cursor = editor.getCursor()
+ var text = []
+ for (var i = 0; i < cursor.line; i++) { // contain current line
+ text.push(editor.getLine(i))
+ }
+ text = text.join('\n') + '\n' + editor.getLine(cursor.line).slice(0, cursor.ch)
+ // console.log(text);
+ return method(text)
+}
+
+function checkBelow (method) {
+ var cursor = editor.getCursor()
+ var count = editor.lineCount()
+ var text = []
+ for (var i = cursor.line + 1; i < count; i++) { // contain current line
+ text.push(editor.getLine(i))
+ }
+ text = editor.getLine(cursor.line).slice(cursor.ch) + '\n' + text.join('\n')
+ // console.log(text);
+ return method(text)
+}
+
+function matchInCode (text) {
+ var match
+ match = text.match(/`{3,}/g)
+ if (match && match.length % 2) {
+ return true
+ } else {
+ match = text.match(/`/g)
if (match && match.length % 2) {
- return true;
+ return true
} else {
- match = text.match(/`/g);
- if (match && match.length % 2) {
- return true;
- } else {
- return false;
- }
+ return false
}
+ }
}
-var isInContainer = false;
-var isInContainerSyntax = false;
+var isInContainer = false
+var isInContainerSyntax = false
-function checkInContainer() {
- isInContainer = checkAbove(matchInContainer) && !checkInIndentCode();
+function checkInContainer () {
+ isInContainer = checkAbove(matchInContainer) && !checkInIndentCode()
}
-function checkInContainerSyntax() {
+function checkInContainerSyntax () {
// if line starts with :::, it's in container syntax
- var line = editor.getLine(editor.getCursor().line);
- isInContainerSyntax = (line.substr(0, 3) === ':::');
+ var line = editor.getLine(editor.getCursor().line)
+ isInContainerSyntax = (line.substr(0, 3) === ':::')
}
-function matchInContainer(text) {
- var match;
- match = text.match(/:{3,}/g);
- if (match && match.length % 2) {
- return true;
- } else {
- return false;
- }
+function matchInContainer (text) {
+ var match
+ match = text.match(/:{3,}/g)
+ if (match && match.length % 2) {
+ return true
+ } else {
+ return false
+ }
}
$(editor.getInputField())
.textcomplete([
- { // emoji strategy
- match: /(^|\n|\s)\B:([\-+\w]*)$/,
- search: function (term, callback) {
- var line = editor.getLine(editor.getCursor().line);
- term = line.match(this.match)[2];
- var list = [];
- $.map(emojify.emojiNames, function (emoji) {
- if (emoji.indexOf(term) === 0) //match at first character
- list.push(emoji);
- });
- $.map(emojify.emojiNames, function (emoji) {
- if (emoji.indexOf(term) !== -1) //match inside the word
- list.push(emoji);
- });
- callback(list);
- },
- template: function (value) {
- return '<img class="emoji" src="' + serverurl + '/build/emojify.js/dist/images/basic/' + value + '.png"></img> ' + value;
- },
- replace: function (value) {
- return '$1:' + value + ': ';
- },
- index: 1,
- context: function (text) {
- checkInCode();
- checkInContainer();
- checkInContainerSyntax();
- return !isInCode && !isInContainerSyntax;
+ { // emoji strategy
+ match: /(^|\n|\s)\B:([-+\w]*)$/,
+ search: function (term, callback) {
+ var line = editor.getLine(editor.getCursor().line)
+ term = line.match(this.match)[2]
+ var list = []
+ $.map(window.emojify.emojiNames, function (emoji) {
+ if (emoji.indexOf(term) === 0) { // match at first character
+ list.push(emoji)
}
- },
- { // Code block language strategy
- langs: supportCodeModes,
- charts: supportCharts,
- match: /(^|\n)```(\w+)$/,
- search: function (term, callback) {
- var line = editor.getLine(editor.getCursor().line);
- term = line.match(this.match)[2];
- var list = [];
- $.map(this.langs, function (lang) {
- if (lang.indexOf(term) === 0 && lang !== term)
- list.push(lang);
- });
- $.map(this.charts, function (chart) {
- if (chart.indexOf(term) === 0 && chart !== term)
- list.push(chart);
- });
- callback(list);
- },
- replace: function (lang) {
- var ending = '';
- if (!checkBelow(matchInCode)) {
- ending = '\n\n```';
- }
- if (this.langs.indexOf(lang) !== -1)
- return '$1```' + lang + '=' + ending;
- else if (this.charts.indexOf(lang) !== -1)
- return '$1```' + lang + ending;
- },
- done: function () {
- var cursor = editor.getCursor();
- var text = [];
- text.push(editor.getLine(cursor.line - 1));
- text.push(editor.getLine(cursor.line));
- text = text.join('\n');
- //console.log(text);
- if (text == '\n```')
- editor.doc.cm.execCommand("goLineUp");
- },
- context: function (text) {
- return isInCode;
+ })
+ $.map(window.emojify.emojiNames, function (emoji) {
+ if (emoji.indexOf(term) !== -1) { // match inside the word
+ list.push(emoji)
}
+ })
+ callback(list)
},
- { // Container strategy
- containers: supportContainers,
- match: /(^|\n):::(\s*)(\w*)$/,
- search: function (term, callback) {
- var line = editor.getLine(editor.getCursor().line);
- term = line.match(this.match)[3].trim();
- var list = [];
- $.map(this.containers, function (container) {
- if (container.indexOf(term) === 0 && container !== term)
- list.push(container);
- });
- callback(list);
- },
- replace: function (lang) {
- var ending = '';
- if (!checkBelow(matchInContainer)) {
- ending = '\n\n:::';
- }
- if (this.containers.indexOf(lang) !== -1)
- return '$1:::$2' + lang + ending;
- },
- done: function () {
- var cursor = editor.getCursor();
- var text = [];
- text.push(editor.getLine(cursor.line - 1));
- text.push(editor.getLine(cursor.line));
- text = text.join('\n');
- //console.log(text);
- if (text == '\n:::')
- editor.doc.cm.execCommand("goLineUp");
- },
- context: function (text) {
- return !isInCode && isInContainer;
- }
+ template: function (value) {
+ return '<img class="emoji" src="' + serverurl + '/build/emojify.js/dist/images/basic/' + value + '.png"></img> ' + value
},
- { //header
- match: /(?:^|\n)(\s{0,3})(#{1,6}\w*)$/,
- search: function (term, callback) {
- callback($.map(supportHeaders, function (header) {
- return header.search.indexOf(term) === 0 ? header.text : null;
- }));
- },
- replace: function (value) {
- return '$1' + value;
- },
- context: function (text) {
- return !isInCode;
- }
+ replace: function (value) {
+ return '$1:' + value + ': '
},
- { //extra tags for blockquote
- match: /(?:^|\n|\s)(\>.*|\s|)((\^|)\[(\^|)\](\[\]|\(\)|\:|)\s*\w*)$/,
- search: function (term, callback) {
- var line = editor.getLine(editor.getCursor().line);
- var quote = line.match(this.match)[1].trim();
- var list = [];
- if (quote.indexOf('>') == 0) {
- $.map(supportExtraTags, function (extratag) {
- if (extratag.search.indexOf(term) === 0)
- list.push(extratag.command());
- });
- }
- $.map(supportReferrals, function (referral) {
- if (referral.search.indexOf(term) === 0)
- list.push(referral.text);
- })
- callback(list);
- },
- replace: function (value) {
- return '$1' + value;
- },
- context: function (text) {
- return !isInCode;
- }
+ index: 1,
+ context: function (text) {
+ checkInCode()
+ checkInContainer()
+ checkInContainerSyntax()
+ return !isInCode && !isInContainerSyntax
+ }
+ },
+ { // Code block language strategy
+ langs: supportCodeModes,
+ charts: supportCharts,
+ match: /(^|\n)```(\w+)$/,
+ search: function (term, callback) {
+ var line = editor.getLine(editor.getCursor().line)
+ term = line.match(this.match)[2]
+ var list = []
+ $.map(this.langs, function (lang) {
+ if (lang.indexOf(term) === 0 && lang !== term) { list.push(lang) }
+ })
+ $.map(this.charts, function (chart) {
+ if (chart.indexOf(term) === 0 && chart !== term) { list.push(chart) }
+ })
+ callback(list)
},
- { //extra tags for list
- match: /(^[>\s]*[\-\+\*]\s(?:\[[x ]\]|.*))(\[\])(\w*)$/,
- search: function (term, callback) {
- var list = [];
- $.map(supportExtraTags, function (extratag) {
- if (extratag.search.indexOf(term) === 0)
- list.push(extratag.command());
- });
- $.map(supportReferrals, function (referral) {
- if (referral.search.indexOf(term) === 0)
- list.push(referral.text);
- })
- callback(list);
- },
- replace: function (value) {
- return '$1' + value;
- },
- context: function (text) {
- return !isInCode;
- }
+ replace: function (lang) {
+ var ending = ''
+ if (!checkBelow(matchInCode)) {
+ ending = '\n\n```'
+ }
+ if (this.langs.indexOf(lang) !== -1) { return '$1```' + lang + '=' + ending } else if (this.charts.indexOf(lang) !== -1) { return '$1```' + lang + ending }
},
- { //referral
- match: /(^\s*|\n|\s{2})((\[\]|\[\]\[\]|\[\]\(\)|\!|\!\[\]|\!\[\]\[\]|\!\[\]\(\))\s*\w*)$/,
- search: function (term, callback) {
- callback($.map(supportReferrals, function (referral) {
- return referral.search.indexOf(term) === 0 ? referral.text : null;
- }));
- },
- replace: function (value) {
- return '$1' + value;
- },
- context: function (text) {
- return !isInCode;
- }
+ done: function () {
+ var cursor = editor.getCursor()
+ var text = []
+ text.push(editor.getLine(cursor.line - 1))
+ text.push(editor.getLine(cursor.line))
+ text = text.join('\n')
+ // console.log(text);
+ if (text === '\n```') { editor.doc.cm.execCommand('goLineUp') }
},
- { //externals
- match: /(^|\n|\s)\{\}(\w*)$/,
- search: function (term, callback) {
- callback($.map(supportExternals, function (external) {
- return external.search.indexOf(term) === 0 ? external.text : null;
- }));
- },
- replace: function (value) {
- return '$1' + value;
- },
- context: function (text) {
- return !isInCode;
- }
- }
- ], {
- appendTo: $('.cursor-menu')
- })
- .on({
- 'textComplete:beforeSearch': function (e) {
- //NA
+ context: function (text) {
+ return isInCode
+ }
+ },
+ { // Container strategy
+ containers: supportContainers,
+ match: /(^|\n):::(\s*)(\w*)$/,
+ search: function (term, callback) {
+ var line = editor.getLine(editor.getCursor().line)
+ term = line.match(this.match)[3].trim()
+ var list = []
+ $.map(this.containers, function (container) {
+ if (container.indexOf(term) === 0 && container !== term) { list.push(container) }
+ })
+ callback(list)
},
- 'textComplete:afterSearch': function (e) {
- checkCursorMenu();
+ replace: function (lang) {
+ var ending = ''
+ if (!checkBelow(matchInContainer)) {
+ ending = '\n\n:::'
+ }
+ if (this.containers.indexOf(lang) !== -1) { return '$1:::$2' + lang + ending }
},
- 'textComplete:select': function (e, value, strategy) {
- //NA
+ done: function () {
+ var cursor = editor.getCursor()
+ var text = []
+ text.push(editor.getLine(cursor.line - 1))
+ text.push(editor.getLine(cursor.line))
+ text = text.join('\n')
+ // console.log(text);
+ if (text === '\n:::') { editor.doc.cm.execCommand('goLineUp') }
},
- 'textComplete:show': function (e) {
- $(this).data('autocompleting', true);
- editor.setOption("extraKeys", {
- "Up": function () {
- return false;
- },
- "Right": function () {
- editor.doc.cm.execCommand("goCharRight");
- },
- "Down": function () {
- return false;
- },
- "Left": function () {
- editor.doc.cm.execCommand("goCharLeft");
- },
- "Enter": function () {
- return false;
- },
- "Backspace": function () {
- editor.doc.cm.execCommand("delCharBefore");
- }
- });
+ context: function (text) {
+ return !isInCode && isInContainer
+ }
+ },
+ { // header
+ match: /(?:^|\n)(\s{0,3})(#{1,6}\w*)$/,
+ search: function (term, callback) {
+ callback($.map(supportHeaders, function (header) {
+ return header.search.indexOf(term) === 0 ? header.text : null
+ }))
+ },
+ replace: function (value) {
+ return '$1' + value
+ },
+ context: function (text) {
+ return !isInCode
+ }
+ },
+ { // extra tags for blockquote
+ match: /(?:^|\n|\s)(>.*|\s|)((\^|)\[(\^|)\](\[\]|\(\)|:|)\s*\w*)$/,
+ search: function (term, callback) {
+ var line = editor.getLine(editor.getCursor().line)
+ var quote = line.match(this.match)[1].trim()
+ var list = []
+ if (quote.indexOf('>') === 0) {
+ $.map(supportExtraTags, function (extratag) {
+ if (extratag.search.indexOf(term) === 0) { list.push(extratag.command()) }
+ })
+ }
+ $.map(supportReferrals, function (referral) {
+ if (referral.search.indexOf(term) === 0) { list.push(referral.text) }
+ })
+ callback(list)
+ },
+ replace: function (value) {
+ return '$1' + value
+ },
+ context: function (text) {
+ return !isInCode
+ }
+ },
+ { // extra tags for list
+ match: /(^[>\s]*[-+*]\s(?:\[[x ]\]|.*))(\[\])(\w*)$/,
+ search: function (term, callback) {
+ var list = []
+ $.map(supportExtraTags, function (extratag) {
+ if (extratag.search.indexOf(term) === 0) { list.push(extratag.command()) }
+ })
+ $.map(supportReferrals, function (referral) {
+ if (referral.search.indexOf(term) === 0) { list.push(referral.text) }
+ })
+ callback(list)
},
- 'textComplete:hide': function (e) {
- $(this).data('autocompleting', false);
- editor.setOption("extraKeys", defaultExtraKeys);
+ replace: function (value) {
+ return '$1' + value
+ },
+ context: function (text) {
+ return !isInCode
+ }
+ },
+ { // referral
+ match: /(^\s*|\n|\s{2})((\[\]|\[\]\[\]|\[\]\(\)|!|!\[\]|!\[\]\[\]|!\[\]\(\))\s*\w*)$/,
+ search: function (term, callback) {
+ callback($.map(supportReferrals, function (referral) {
+ return referral.search.indexOf(term) === 0 ? referral.text : null
+ }))
+ },
+ replace: function (value) {
+ return '$1' + value
+ },
+ context: function (text) {
+ return !isInCode
+ }
+ },
+ { // externals
+ match: /(^|\n|\s)\{\}(\w*)$/,
+ search: function (term, callback) {
+ callback($.map(supportExternals, function (external) {
+ return external.search.indexOf(term) === 0 ? external.text : null
+ }))
+ },
+ replace: function (value) {
+ return '$1' + value
+ },
+ context: function (text) {
+ return !isInCode
}
- });
+ }
+ ], {
+ appendTo: $('.cursor-menu')
+ })
+ .on({
+ 'textComplete:beforeSearch': function (e) {
+ // NA
+ },
+ 'textComplete:afterSearch': function (e) {
+ checkCursorMenu()
+ },
+ 'textComplete:select': function (e, value, strategy) {
+ // NA
+ },
+ 'textComplete:show': function (e) {
+ $(this).data('autocompleting', true)
+ editor.setOption('extraKeys', {
+ 'Up': function () {
+ return false
+ },
+ 'Right': function () {
+ editor.doc.cm.execCommand('goCharRight')
+ },
+ 'Down': function () {
+ return false
+ },
+ 'Left': function () {
+ editor.doc.cm.execCommand('goCharLeft')
+ },
+ 'Enter': function () {
+ return false
+ },
+ 'Backspace': function () {
+ editor.doc.cm.execCommand('delCharBefore')
+ }
+ })
+ },
+ 'textComplete:hide': function (e) {
+ $(this).data('autocompleting', false)
+ editor.setOption('extraKeys', defaultExtraKeys)
+ }
+ })
diff --git a/public/js/lib/common/login.js b/public/js/lib/common/login.js
index 58fa55c6..18cd377d 100644
--- a/public/js/lib/common/login.js
+++ b/public/js/lib/common/login.js
@@ -1,89 +1,92 @@
-import { serverurl } from '../config';
+/* eslint-env browser, jquery */
+/* global Cookies */
-let checkAuth = false;
-let profile = null;
-let lastLoginState = getLoginState();
-let lastUserId = getUserId();
-var loginStateChangeEvent = null;
+import { serverurl } from '../config'
-export function setloginStateChangeEvent(func) {
- loginStateChangeEvent = func;
+let checkAuth = false
+let profile = null
+let lastLoginState = getLoginState()
+let lastUserId = getUserId()
+var loginStateChangeEvent = null
+
+export function setloginStateChangeEvent (func) {
+ loginStateChangeEvent = func
}
-export function resetCheckAuth() {
- checkAuth = false;
+export function resetCheckAuth () {
+ checkAuth = false
}
-export function setLoginState(bool, id) {
- Cookies.set('loginstate', bool, {
- expires: 365
- });
- if (id) {
- Cookies.set('userid', id, {
- expires: 365
- });
- } else {
- Cookies.remove('userid');
- }
- lastLoginState = bool;
- lastUserId = id;
- checkLoginStateChanged();
+export function setLoginState (bool, id) {
+ Cookies.set('loginstate', bool, {
+ expires: 365
+ })
+ if (id) {
+ Cookies.set('userid', id, {
+ expires: 365
+ })
+ } else {
+ Cookies.remove('userid')
+ }
+ lastLoginState = bool
+ lastUserId = id
+ checkLoginStateChanged()
}
-export function checkLoginStateChanged() {
- if (getLoginState() != lastLoginState || getUserId() != lastUserId) {
- if (loginStateChangeEvent) setTimeout(loginStateChangeEvent, 100);
- return true;
- } else {
- return false;
- }
+export function checkLoginStateChanged () {
+ if (getLoginState() !== lastLoginState || getUserId() !== lastUserId) {
+ if (loginStateChangeEvent) setTimeout(loginStateChangeEvent, 100)
+ return true
+ } else {
+ return false
+ }
}
-export function getLoginState() {
- const state = Cookies.get('loginstate');
- return state === "true" || state === true;
+export function getLoginState () {
+ const state = Cookies.get('loginstate')
+ return state === 'true' || state === true
}
-export function getUserId() {
- return Cookies.get('userid');
+export function getUserId () {
+ return Cookies.get('userid')
}
-export function clearLoginState() {
- Cookies.remove('loginstate');
+export function clearLoginState () {
+ Cookies.remove('loginstate')
}
-export function checkIfAuth(yesCallback, noCallback) {
- const cookieLoginState = getLoginState();
- if (checkLoginStateChanged()) checkAuth = false;
- if (!checkAuth || typeof cookieLoginState == 'undefined') {
- $.get(`${serverurl}/me`)
+export function checkIfAuth (yesCallback, noCallback) {
+ const cookieLoginState = getLoginState()
+ if (checkLoginStateChanged()) checkAuth = false
+ if (!checkAuth || typeof cookieLoginState === 'undefined') {
+ $.get(`${serverurl}/me`)
.done(data => {
- if (data && data.status == 'ok') {
- profile = data;
- yesCallback(profile);
- setLoginState(true, data.id);
- } else {
- noCallback();
- setLoginState(false);
- }
+ if (data && data.status === 'ok') {
+ profile = data
+ yesCallback(profile)
+ setLoginState(true, data.id)
+ } else {
+ noCallback()
+ setLoginState(false)
+ }
})
.fail(() => {
- noCallback();
+ noCallback()
})
.always(() => {
- checkAuth = true;
- });
- } else if (cookieLoginState) {
- yesCallback(profile);
- } else {
- noCallback();
- }
+ checkAuth = true
+ })
+ } else if (cookieLoginState) {
+ yesCallback(profile)
+ } else {
+ noCallback()
+ }
}
export default {
- checkAuth,
- profile,
- lastLoginState,
- lastUserId,
- loginStateChangeEvent
-};
+ checkAuth,
+ profile,
+ lastLoginState,
+ lastUserId,
+ loginStateChangeEvent
+}
diff --git a/public/js/lib/config/index.js b/public/js/lib/config/index.js
index 2b73679f..1ea7a7ab 100644
--- a/public/js/lib/config/index.js
+++ b/public/js/lib/config/index.js
@@ -1,19 +1,19 @@
-import configJson from '../../../../config.json'; // root path json config
+import configJson from '../../../../config.json' // root path json config
-const config = 'production' === process.env.NODE_ENV ? configJson.production : configJson.development;
+const config = process.env.NODE_ENV === 'production' ? configJson.production : configJson.development
-export const GOOGLE_API_KEY = (config.google && config.google.apiKey) || '';
-export const GOOGLE_CLIENT_ID = (config.google && config.google.clientID) || '';
-export const DROPBOX_APP_KEY = (config.dropbox && config.dropbox.appKey) || '';
+export const GOOGLE_API_KEY = (config.google && config.google.apiKey) || ''
+export const GOOGLE_CLIENT_ID = (config.google && config.google.clientID) || ''
+export const DROPBOX_APP_KEY = (config.dropbox && config.dropbox.appKey) || ''
-export const domain = config.domain || ''; // domain name
-export const urlpath = config.urlpath || ''; // sub url path, like: www.example.com/<urlpath>
-export const debug = config.debug || false;
+export const domain = config.domain || '' // domain name
+export const urlpath = config.urlpath || '' // sub url path, like: www.example.com/<urlpath>
+export const debug = config.debug || false
-export const port = window.location.port;
-export const serverurl = `${window.location.protocol}//${domain ? domain : window.location.hostname}${port ? ':' + port : ''}${urlpath ? '/' + urlpath : ''}`;
-window.serverurl = serverurl;
-export const noteid = urlpath ? window.location.pathname.slice(urlpath.length + 1, window.location.pathname.length).split('/')[1] : window.location.pathname.split('/')[1];
-export const noteurl = `${serverurl}/${noteid}`;
+export const port = window.location.port
+export const serverurl = `${window.location.protocol}//${domain || window.location.hostname}${port ? ':' + port : ''}${urlpath ? '/' + urlpath : ''}`
+window.serverurl = serverurl
+export const noteid = urlpath ? window.location.pathname.slice(urlpath.length + 1, window.location.pathname.length).split('/')[1] : window.location.pathname.split('/')[1]
+export const noteurl = `${serverurl}/${noteid}`
-export const version = '0.5.0';
+export const version = '0.5.0'
diff --git a/public/js/locale.js b/public/js/locale.js
index e6d11cd2..2a2c1814 100644
--- a/public/js/locale.js
+++ b/public/js/locale.js
@@ -1,26 +1,28 @@
-var lang = "en";
-var userLang = navigator.language || navigator.userLanguage;
-var userLangCode = userLang.split('-')[0];
-var userCountryCode = userLang.split('-')[1];
-var locale = $('.ui-locale');
-var supportLangs = [];
-$(".ui-locale option").each(function() {
- supportLangs.push($(this).val());
-});
+/* eslint-env browser, jquery */
+/* global Cookies */
+
+var lang = 'en'
+var userLang = navigator.language || navigator.userLanguage
+var userLangCode = userLang.split('-')[0]
+var locale = $('.ui-locale')
+var supportLangs = []
+$('.ui-locale option').each(function () {
+ supportLangs.push($(this).val())
+})
if (Cookies.get('locale')) {
- lang = Cookies.get('locale');
+ lang = Cookies.get('locale')
} else if (supportLangs.indexOf(userLang) !== -1) {
- lang = supportLangs[supportLangs.indexOf(userLang)];
+ lang = supportLangs[supportLangs.indexOf(userLang)]
} else if (supportLangs.indexOf(userLangCode) !== -1) {
- lang = supportLangs[supportLangs.indexOf(userLangCode)];
+ lang = supportLangs[supportLangs.indexOf(userLangCode)]
}
-locale.val(lang);
-$('select.ui-locale option[value="' + lang + '"]').attr('selected','selected');
+locale.val(lang)
+$('select.ui-locale option[value="' + lang + '"]').attr('selected', 'selected')
-locale.change(function() {
- Cookies.set('locale', $(this).val(), {
- expires: 365
- });
- window.location.reload();
-});
+locale.change(function () {
+ Cookies.set('locale', $(this).val(), {
+ expires: 365
+ })
+ window.location.reload()
+})
diff --git a/public/js/pretty.js b/public/js/pretty.js
index 18d0dc0d..718941a8 100644
--- a/public/js/pretty.js
+++ b/public/js/pretty.js
@@ -1,8 +1,11 @@
-require('../css/extra.css');
-require('../css/slide-preview.css');
-require('../css/site.css');
+/* eslint-env browser, jquery */
+/* global refreshView */
-require('highlight.js/styles/github-gist.css');
+require('../css/extra.css')
+require('../css/slide-preview.css')
+require('../css/site.css')
+
+require('highlight.js/styles/github-gist.css')
import {
autoLinkify,
@@ -16,126 +19,126 @@ import {
scrollToHash,
smoothHashScroll,
updateLastChange
-} from './extra';
+} from './extra'
-import { preventXSS } from './render';
+import { preventXSS } from './render'
-const markdown = $("#doc.markdown-body");
-const text = markdown.text();
-const lastMeta = md.meta;
-md.meta = {};
-delete md.metaError;
-let rendered = md.render(text);
+const markdown = $('#doc.markdown-body')
+const text = markdown.text()
+const lastMeta = md.meta
+md.meta = {}
+delete md.metaError
+let rendered = md.render(text)
if (md.meta.type && md.meta.type === 'slide') {
- const slideOptions = {
- separator: '^(\r\n?|\n)---(\r\n?|\n)$',
- verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
- };
- const slides = RevealMarkdown.slidify(text, slideOptions);
- markdown.html(slides);
- RevealMarkdown.initialize();
+ const slideOptions = {
+ separator: '^(\r\n?|\n)---(\r\n?|\n)$',
+ verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
+ }
+ const slides = window.RevealMarkdown.slidify(text, slideOptions)
+ markdown.html(slides)
+ window.RevealMarkdown.initialize()
// prevent XSS
- markdown.html(preventXSS(markdown.html()));
- markdown.addClass('slides');
+ markdown.html(preventXSS(markdown.html()))
+ markdown.addClass('slides')
} else {
- if (lastMeta.type && lastMeta.type === 'slide') {
- refreshView();
- markdown.removeClass('slides');
- }
+ if (lastMeta.type && lastMeta.type === 'slide') {
+ refreshView()
+ markdown.removeClass('slides')
+ }
// only render again when meta changed
- if (JSON.stringify(md.meta) != JSON.stringify(lastMeta)) {
- parseMeta(md, null, markdown, $('#ui-toc'), $('#ui-toc-affix'));
- rendered = md.render(text);
- }
+ if (JSON.stringify(md.meta) !== JSON.stringify(lastMeta)) {
+ parseMeta(md, null, markdown, $('#ui-toc'), $('#ui-toc-affix'))
+ rendered = md.render(text)
+ }
// prevent XSS
- rendered = preventXSS(rendered);
- const result = postProcess(rendered);
- markdown.html(result.html());
+ rendered = preventXSS(rendered)
+ const result = postProcess(rendered)
+ markdown.html(result.html())
}
-$(document.body).show();
+$(document.body).show()
-finishView(markdown);
-autoLinkify(markdown);
-deduplicatedHeaderId(markdown);
-renderTOC(markdown);
-generateToc('ui-toc');
-generateToc('ui-toc-affix');
-smoothHashScroll();
-createtime = lastchangeui.time.attr('data-createtime');
-lastchangetime = lastchangeui.time.attr('data-updatetime');
-updateLastChange();
+finishView(markdown)
+autoLinkify(markdown)
+deduplicatedHeaderId(markdown)
+renderTOC(markdown)
+generateToc('ui-toc')
+generateToc('ui-toc-affix')
+smoothHashScroll()
+window.createtime = window.lastchangeui.time.attr('data-createtime')
+window.lastchangetime = window.lastchangeui.time.attr('data-updatetime')
+updateLastChange()
-const url = window.location.pathname;
-$('.ui-edit').attr('href', `${url}/edit`);
-const toc = $('.ui-toc');
-const tocAffix = $('.ui-affix-toc');
-const tocDropdown = $('.ui-toc-dropdown');
-//toc
+const url = window.location.pathname
+$('.ui-edit').attr('href', `${url}/edit`)
+const toc = $('.ui-toc')
+const tocAffix = $('.ui-affix-toc')
+const tocDropdown = $('.ui-toc-dropdown')
+// toc
tocDropdown.click(e => {
- e.stopPropagation();
-});
+ e.stopPropagation()
+})
-let enoughForAffixToc = true;
+let 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 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
- const paddingRight = parseFloat(markdown.css('padding-right'));
- const right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
- toc.css('right', `${right}px`);
- //affix toc left
- let newbool;
- const rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
- //for ipad or wider device
- if (rightMargin >= 133) {
- newbool = true;
- const affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
- const left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
- tocAffix.css('left', `${left}px`);
- } else {
- newbool = false;
- }
- if (newbool != enoughForAffixToc) {
- enoughForAffixToc = newbool;
- generateScrollspy();
- }
+function windowResize () {
+ // toc right
+ const paddingRight = parseFloat(markdown.css('padding-right'))
+ const right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight))
+ toc.css('right', `${right}px`)
+ // affix toc left
+ let newbool
+ const rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2
+ // for ipad or wider device
+ if (rightMargin >= 133) {
+ newbool = true
+ const affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2
+ const left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin
+ tocAffix.css('left', `${left}px`)
+ } else {
+ newbool = false
+ }
+ if (newbool !== enoughForAffixToc) {
+ enoughForAffixToc = newbool
+ generateScrollspy()
+ }
}
$(window).resize(() => {
- windowResize();
-});
+ windowResize()
+})
$(document).ready(() => {
- windowResize();
- generateScrollspy();
- setTimeout(scrollToHash, 0);
- //tooltip
- $('[data-toggle="tooltip"]').tooltip();
-});
+ windowResize()
+ generateScrollspy()
+ setTimeout(scrollToHash, 0)
+ // tooltip
+ $('[data-toggle="tooltip"]').tooltip()
+})
-export function scrollToTop() {
- $('body, html').stop(true, true).animate({
- scrollTop: 0
- }, 100, "linear");
+export function scrollToTop () {
+ $('body, html').stop(true, true).animate({
+ scrollTop: 0
+ }, 100, 'linear')
}
-export function scrollToBottom() {
- $('body, html').stop(true, true).animate({
- scrollTop: $(document.body)[0].scrollHeight
- }, 100, "linear");
+export function scrollToBottom () {
+ $('body, html').stop(true, true).animate({
+ scrollTop: $(document.body)[0].scrollHeight
+ }, 100, 'linear')
}
-window.scrollToTop = scrollToTop;
-window.scrollToBottom = scrollToBottom;
+window.scrollToTop = scrollToTop
+window.scrollToBottom = scrollToBottom
diff --git a/public/js/render.js b/public/js/render.js
index 5d6d0aa2..61663a4b 100644
--- a/public/js/render.js
+++ b/public/js/render.js
@@ -1,62 +1,64 @@
+/* eslint-env browser, jquery */
+/* global filterXSS */
// allow some attributes
-var whiteListAttr = ['id', 'class', 'style'];
-window.whiteListAttr = whiteListAttr;
+var whiteListAttr = ['id', 'class', 'style']
+window.whiteListAttr = whiteListAttr
// allow link starts with '.', '/' and custom protocol with '://'
-var linkRegex = /^([\w|-]+:\/\/)|^([\.|\/])+/;
+var linkRegex = /^([\w|-]+:\/\/)|^([.|/])+/
// allow data uri, from https://gist.github.com/bgrins/6194623
-var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*)\s*$/i;
+var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
// custom white list
-var whiteList = filterXSS.whiteList;
+var whiteList = filterXSS.whiteList
// allow ol specify start number
-whiteList['ol'] = ['start'];
+whiteList['ol'] = ['start']
// allow li specify value number
-whiteList['li'] = ['value'];
+whiteList['li'] = ['value']
// allow style tag
-whiteList['style'] = [];
+whiteList['style'] = []
// allow kbd tag
-whiteList['kbd'] = [];
+whiteList['kbd'] = []
// allow ifram tag with some safe attributes
-whiteList['iframe'] = ['allowfullscreen', 'name', 'referrerpolicy', 'sandbox', 'src', 'srcdoc', 'width', 'height'];
+whiteList['iframe'] = ['allowfullscreen', 'name', 'referrerpolicy', 'sandbox', 'src', 'srcdoc', 'width', 'height']
// allow summary tag
-whiteList['summary'] = [];
+whiteList['summary'] = []
var filterXSSOptions = {
- allowCommentTag: true,
- whiteList: whiteList,
- escapeHtml: function (html) {
+ allowCommentTag: true,
+ whiteList: whiteList,
+ escapeHtml: function (html) {
// allow html comment in multiple lines
- return html.replace(/<(.*?)>/g, '&lt;$1&gt;');
- },
- onIgnoreTag: function (tag, html, options) {
+ return html.replace(/<(.*?)>/g, '&lt;$1&gt;')
+ },
+ onIgnoreTag: function (tag, html, options) {
// allow comment tag
- if (tag == "!--") {
+ if (tag === '!--') {
// do not filter its attributes
- return html;
- }
- },
- onTagAttr: function (tag, name, value, isWhiteAttr) {
+ return html
+ }
+ },
+ onTagAttr: function (tag, name, value, isWhiteAttr) {
// allow href and src that match linkRegex
- if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
- return name + '="' + filterXSS.escapeAttrValue(value) + '"';
- }
+ if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
+ return name + '="' + filterXSS.escapeAttrValue(value) + '"'
+ }
// allow data uri in img src
- if (isWhiteAttr && (tag == "img" && name === 'src') && dataUriRegex.test(value)) {
- return name + '="' + filterXSS.escapeAttrValue(value) + '"';
- }
- },
- onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
+ if (isWhiteAttr && (tag === 'img' && name === 'src') && dataUriRegex.test(value)) {
+ return name + '="' + filterXSS.escapeAttrValue(value) + '"'
+ }
+ },
+ onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
// allow attr start with 'data-' or in the whiteListAttr
- if (name.substr(0, 5) === 'data-' || whiteListAttr.indexOf(name) !== -1) {
+ if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) {
// escape its value using built-in escapeAttrValue function
- return name + '="' + filterXSS.escapeAttrValue(value) + '"';
- }
+ return name + '="' + filterXSS.escapeAttrValue(value) + '"'
}
-};
+ }
+}
-function preventXSS(html) {
- return filterXSS(html, filterXSSOptions);
+function preventXSS (html) {
+ return filterXSS(html, filterXSSOptions)
}
-window.preventXSS = preventXSS;
+window.preventXSS = preventXSS
module.exports = {
preventXSS: preventXSS
diff --git a/public/js/reveal-markdown.js b/public/js/reveal-markdown.js
index 3c3e1f5b..eca148d8 100755
--- a/public/js/reveal-markdown.js
+++ b/public/js/reveal-markdown.js
@@ -1,396 +1,355 @@
+/* eslint-env browser, jquery */
+
+import { preventXSS } from './render'
+import { md } from './extra'
+
/**
* The reveal.js markdown plugin. Handles parsing of
* markdown inside of presentations as well as loading
* of external markdown documents.
*/
-(function( root, factory ) {
- if( typeof exports === 'object' ) {
- module.exports = factory();
- }
- else {
- // Browser globals (root is window)
- root.RevealMarkdown = factory();
- root.RevealMarkdown.initialize();
- }
-}( this, function() {
-
- var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$',
- DEFAULT_NOTES_SEPARATOR = 'note:',
- DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
- DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
-
- var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__';
-
-
- /**
- * Retrieves the markdown contents of a slide section
- * element. Normalizes leading tabs/whitespace.
- */
- function getMarkdownFromSlide( section ) {
-
- var template = section.querySelector( 'script' );
-
- // strip leading whitespace so it isn't evaluated as code
- var text = ( template || section ).textContent;
-
- // restore script end tags
- text = text.replace( new RegExp( SCRIPT_END_PLACEHOLDER, 'g' ), '</script>' );
-
- var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
- leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
-
- if( leadingTabs > 0 ) {
- text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
- }
- else if( leadingWs > 1 ) {
- text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
- }
-
- return text;
-
- }
-
- /**
- * Given a markdown slide section element, this will
- * return all arguments that aren't related to markdown
- * parsing. Used to forward any other user-defined arguments
- * to the output markdown slide.
- */
- function getForwardedAttributes( section ) {
-
- var attributes = section.attributes;
- var result = [];
-
- for( var i = 0, len = attributes.length; i < len; i++ ) {
- var name = attributes[i].name,
- value = attributes[i].value;
-
- // disregard attributes that are used for markdown loading/parsing
- if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
-
- if( value ) {
- result.push( name + '="' + value + '"' );
- }
- else {
- result.push( name );
- }
- }
-
- return result.join( ' ' );
-
- }
-
- /**
- * Inspects the given options and fills out default
- * values for what's not defined.
- */
- function getSlidifyOptions( options ) {
-
- options = options || {};
- options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
- options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
- options.attributes = options.attributes || '';
-
- return options;
-
- }
-
- /**
- * Helper function for constructing a markdown slide.
- */
- function createMarkdownSlide( content, options ) {
-
- options = getSlidifyOptions( options );
-
- var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
-
- if( notesMatch.length === 2 ) {
- content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>';
- }
-
- // prevent script end tags in the content from interfering
- // with parsing
- content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER );
-
- return '<script type="text/template">' + content + '</script>';
-
- }
-
- /**
- * Parses a data string into multiple slides based
- * on the passed in separator arguments.
- */
- function slidify( markdown, options ) {
-
- options = getSlidifyOptions( options );
-
- var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
- horizontalSeparatorRegex = new RegExp( options.separator );
-
- var matches,
- lastIndex = 0,
- isHorizontal,
- wasHorizontal = true,
- content,
- sectionStack = [];
-
- // iterate until all blocks between separators are stacked up
- while( matches = separatorRegex.exec( markdown ) ) {
- notes = null;
-
- // determine direction (horizontal by default)
- isHorizontal = horizontalSeparatorRegex.test( matches[0] );
-
- if( !isHorizontal && wasHorizontal ) {
- // create vertical stack
- sectionStack.push( [] );
- }
-
- // pluck slide content from markdown input
- content = markdown.substring( lastIndex, matches.index );
-
- if( isHorizontal && wasHorizontal ) {
- // add to horizontal stack
- sectionStack.push( content );
- }
- else {
- // add to vertical stack
- sectionStack[sectionStack.length-1].push( content );
- }
-
- lastIndex = separatorRegex.lastIndex;
- wasHorizontal = isHorizontal;
- }
-
- // add the remaining slide
- ( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
-
- var markdownSections = '';
-
- // flatten the hierarchical stack, and insert <section data-markdown> tags
- for( var i = 0, len = sectionStack.length; i < len; i++ ) {
- // vertical
- if( sectionStack[i] instanceof Array ) {
- markdownSections += '<section '+ options.attributes +'>';
-
- sectionStack[i].forEach( function( child ) {
- markdownSections += '<section data-markdown>' + createMarkdownSlide( child, options ) + '</section>';
- } );
-
- markdownSections += '</section>';
- }
- else {
- markdownSections += '<section '+ options.attributes +' data-markdown>' + createMarkdownSlide( sectionStack[i], options ) + '</section>';
- }
- }
-
- return markdownSections;
-
- }
-
- /**
- * Parses any current data-markdown slides, splits
- * multi-slide markdown into separate sections and
- * handles loading of external markdown.
- */
- function processSlides() {
-
- var sections = document.querySelectorAll( '[data-markdown]'),
- section;
-
- for( var i = 0, len = sections.length; i < len; i++ ) {
-
- section = sections[i];
-
- if( section.getAttribute( 'data-markdown' ).length ) {
-
- var xhr = new XMLHttpRequest(),
- url = section.getAttribute( 'data-markdown' );
-
- datacharset = section.getAttribute( 'data-charset' );
-
- // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
- if( datacharset != null && datacharset != '' ) {
- xhr.overrideMimeType( 'text/html; charset=' + datacharset );
- }
-
- xhr.onreadystatechange = function() {
- if( xhr.readyState === 4 ) {
- // file protocol yields status code 0 (useful for local debug, mobile applications etc.)
- if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
-
- section.outerHTML = slidify( xhr.responseText, {
- separator: section.getAttribute( 'data-separator' ),
- verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
- notesSeparator: section.getAttribute( 'data-separator-notes' ),
- attributes: getForwardedAttributes( section )
- });
-
- }
- else {
-
- section.outerHTML = '<section data-state="alert">' +
- 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
- 'Check your browser\'s JavaScript console for more details.' +
- '<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
- '</section>';
-
- }
- }
- };
-
- xhr.open( 'GET', url, false );
-
- try {
- xhr.send();
- }
- catch ( e ) {
- alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
- }
-
- }
- else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {
-
- section.outerHTML = slidify( getMarkdownFromSlide( section ), {
- separator: section.getAttribute( 'data-separator' ),
- verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
- notesSeparator: section.getAttribute( 'data-separator-notes' ),
- attributes: getForwardedAttributes( section )
- });
-
- }
- else {
- section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
- }
- }
-
- }
-
- /**
- * Check if a node value has the attributes pattern.
- * If yes, extract it and add that value as one or several attributes
- * the the terget element.
- *
- * You need Cache Killer on Chrome to see the effect on any FOM transformation
- * directly on refresh (F5)
- * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
- */
- function addAttributeInElement( node, elementTarget, separator ) {
-
- var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
- var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
- var nodeValue = node.nodeValue;
- if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
-
- var classes = matches[1];
- nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
- node.nodeValue = nodeValue;
- while( matchesClass = mardownClassRegex.exec( classes ) ) {
- var name = matchesClass[1];
- var value = matchesClass[2];
- if (name.substr(0, 5) === 'data-' || whiteListAttr.indexOf(name) !== -1)
- elementTarget.setAttribute( name, filterXSS.escapeAttrValue(value) );
- }
- return true;
- }
- return false;
- }
-
- /**
- * Add attributes to the parent element of a text node,
- * or the element of an attribute node.
- */
- function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
-
- if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
- previousParentElement = element;
- for( var i = 0; i < element.childNodes.length; i++ ) {
- childElement = element.childNodes[i];
- if ( i > 0 ) {
- j = i - 1;
- while ( j >= 0 ) {
- aPreviousChildElement = element.childNodes[j];
- if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
- previousParentElement = aPreviousChildElement;
- break;
- }
- j = j - 1;
- }
- }
- parentSection = section;
- if( childElement.nodeName == "section" ) {
- parentSection = childElement ;
- previousParentElement = childElement ;
- }
- if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
- addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
- }
- }
- }
-
- if ( element.nodeType == Node.COMMENT_NODE ) {
- if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
- addAttributeInElement( element, section, separatorSectionAttributes );
- }
- }
- }
-
- /**
- * Converts any current data-markdown slides in the
- * DOM to HTML.
- */
- function convertSlides() {
-
- var sections = document.querySelectorAll( '[data-markdown]');
-
- for( var i = 0, len = sections.length; i < len; i++ ) {
-
- var section = sections[i];
-
- // Only parse the same slide once
- if( !section.getAttribute( 'data-markdown-parsed' ) ) {
-
- section.setAttribute( 'data-markdown-parsed', true )
-
- var notes = section.querySelector( 'aside.notes' );
- var markdown = getMarkdownFromSlide( section );
-
- var rendered = md.render(markdown);
- rendered = preventXSS(rendered);
- var result = postProcess(rendered);
- section.innerHTML = result[0].outerHTML;
- addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) ||
- section.parentNode.getAttribute( 'data-element-attributes' ) ||
- DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
- section.getAttribute( 'data-attributes' ) ||
- section.parentNode.getAttribute( 'data-attributes' ) ||
- DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
-
- // If there were notes, we need to re-add them after
- // having overwritten the section's HTML
- if( notes ) {
- section.appendChild( notes );
- }
-
- }
-
- }
-
- }
-
- // API
- return {
-
- initialize: function() {
- processSlides();
- convertSlides();
- },
-
- // TODO: Do these belong in the API?
- processSlides: processSlides,
- convertSlides: convertSlides,
- slidify: slidify
-
- };
-
-}));
+(function (root, factory) {
+ if (typeof exports === 'object') {
+ module.exports = factory()
+ } else {
+ // Browser globals (root is window)
+ root.RevealMarkdown = factory()
+ root.RevealMarkdown.initialize()
+ }
+}(this, function () {
+ var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$'
+ var DEFAULT_NOTES_SEPARATOR = 'note:'
+ var DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\.element\\s*?(.+?)$'
+ var DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\.slide:\\s*?(\\S.+?)$'
+
+ var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__'
+
+ /**
+ * Retrieves the markdown contents of a slide section
+ * element. Normalizes leading tabs/whitespace.
+ */
+ function getMarkdownFromSlide (section) {
+ var template = section.querySelector('script')
+
+ // strip leading whitespace so it isn't evaluated as code
+ var text = (template || section).textContent
+
+ // restore script end tags
+ text = text.replace(new RegExp(SCRIPT_END_PLACEHOLDER, 'g'), '</script>')
+
+ var leadingWs = text.match(/^\n?(\s*)/)[1].length
+ var leadingTabs = text.match(/^\n?(\t*)/)[1].length
+
+ if (leadingTabs > 0) {
+ text = text.replace(new RegExp('\\n?\\t{' + leadingTabs + '}', 'g'), '\n')
+ } else if (leadingWs > 1) {
+ text = text.replace(new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n')
+ }
+
+ return text
+ }
+
+ /**
+ * Given a markdown slide section element, this will
+ * return all arguments that aren't related to markdown
+ * parsing. Used to forward any other user-defined arguments
+ * to the output markdown slide.
+ */
+ function getForwardedAttributes (section) {
+ var attributes = section.attributes
+ var result = []
+
+ for (var i = 0, len = attributes.length; i < len; i++) {
+ var name = attributes[i].name
+ var value = attributes[i].value
+
+ // disregard attributes that are used for markdown loading/parsing
+ if (/data-(markdown|separator|vertical|notes)/gi.test(name)) continue
+
+ if (value) {
+ result.push(name + '="' + value + '"')
+ } else {
+ result.push(name)
+ }
+ }
+
+ return result.join(' ')
+ }
+
+ /**
+ * Inspects the given options and fills out default
+ * values for what's not defined.
+ */
+ function getSlidifyOptions (options) {
+ options = options || {}
+ options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR
+ options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR
+ options.attributes = options.attributes || ''
+
+ return options
+ }
+
+ /**
+ * Helper function for constructing a markdown slide.
+ */
+ function createMarkdownSlide (content, options) {
+ options = getSlidifyOptions(options)
+
+ var notesMatch = content.split(new RegExp(options.notesSeparator, 'mgi'))
+
+ if (notesMatch.length === 2) {
+ content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>'
+ }
+
+ // prevent script end tags in the content from interfering
+ // with parsing
+ content = content.replace(/<\/script>/g, SCRIPT_END_PLACEHOLDER)
+
+ return '<script type="text/template">' + content + '</script>'
+ }
+
+ /**
+ * Parses a data string into multiple slides based
+ * on the passed in separator arguments.
+ */
+ function slidify (markdown, options) {
+ options = getSlidifyOptions(options)
+
+ var separatorRegex = new RegExp(options.separator + (options.verticalSeparator ? '|' + options.verticalSeparator : ''), 'mg')
+ var horizontalSeparatorRegex = new RegExp(options.separator)
+
+ var matches
+ var lastIndex = 0
+ var isHorizontal
+ var wasHorizontal = true
+ var content
+ var sectionStack = []
+
+ // iterate until all blocks between separators are stacked up
+ while ((matches = separatorRegex.exec(markdown)) !== null) {
+ // determine direction (horizontal by default)
+ isHorizontal = horizontalSeparatorRegex.test(matches[0])
+
+ if (!isHorizontal && wasHorizontal) {
+ // create vertical stack
+ sectionStack.push([])
+ }
+
+ // pluck slide content from markdown input
+ content = markdown.substring(lastIndex, matches.index)
+
+ if (isHorizontal && wasHorizontal) {
+ // add to horizontal stack
+ sectionStack.push(content)
+ } else {
+ // add to vertical stack
+ sectionStack[sectionStack.length - 1].push(content)
+ }
+
+ lastIndex = separatorRegex.lastIndex
+ wasHorizontal = isHorizontal
+ }
+
+ // add the remaining slide
+ (wasHorizontal ? sectionStack : sectionStack[sectionStack.length - 1]).push(markdown.substring(lastIndex))
+
+ var markdownSections = ''
+
+ // flatten the hierarchical stack, and insert <section data-markdown> tags
+ for (var i = 0, len = sectionStack.length; i < len; i++) {
+ // vertical
+ if (sectionStack[i] instanceof Array) {
+ markdownSections += '<section ' + options.attributes + '>'
+
+ sectionStack[i].forEach(function (child) {
+ markdownSections += '<section data-markdown>' + createMarkdownSlide(child, options) + '</section>'
+ })
+
+ markdownSections += '</section>'
+ } else {
+ markdownSections += '<section ' + options.attributes + ' data-markdown>' + createMarkdownSlide(sectionStack[i], options) + '</section>'
+ }
+ }
+
+ return markdownSections
+ }
+
+ /**
+ * Parses any current data-markdown slides, splits
+ * multi-slide markdown into separate sections and
+ * handles loading of external markdown.
+ */
+ function processSlides () {
+ var sections = document.querySelectorAll('[data-markdown]')
+ var section
+
+ for (var i = 0, len = sections.length; i < len; i++) {
+ section = sections[i]
+
+ if (section.getAttribute('data-markdown').length) {
+ var xhr = new XMLHttpRequest()
+ var url = section.getAttribute('data-markdown')
+
+ var datacharset = section.getAttribute('data-charset')
+
+ // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
+ if (datacharset !== null && datacharset !== '') {
+ xhr.overrideMimeType('text/html; charset=' + datacharset)
+ }
+
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === 4) {
+ // file protocol yields status code 0 (useful for local debug, mobile applications etc.)
+ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {
+ section.outerHTML = slidify(xhr.responseText, {
+ separator: section.getAttribute('data-separator'),
+ verticalSeparator: section.getAttribute('data-separator-vertical'),
+ notesSeparator: section.getAttribute('data-separator-notes'),
+ attributes: getForwardedAttributes(section)
+ })
+ } else {
+ section.outerHTML = '<section data-state="alert">' +
+ 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
+ 'Check your browser\'s JavaScript console for more details.' +
+ '<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
+ '</section>'
+ }
+ }
+ }
+
+ xhr.open('GET', url, false)
+
+ try {
+ xhr.send()
+ } catch (e) {
+ alert('Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e)
+ }
+ } else if (section.getAttribute('data-separator') || section.getAttribute('data-separator-vertical') || section.getAttribute('data-separator-notes')) {
+ section.outerHTML = slidify(getMarkdownFromSlide(section), {
+ separator: section.getAttribute('data-separator'),
+ verticalSeparator: section.getAttribute('data-separator-vertical'),
+ notesSeparator: section.getAttribute('data-separator-notes'),
+ attributes: getForwardedAttributes(section)
+ })
+ } else {
+ section.innerHTML = createMarkdownSlide(getMarkdownFromSlide(section))
+ }
+ }
+ }
+
+ /**
+ * Check if a node value has the attributes pattern.
+ * If yes, extract it and add that value as one or several attributes
+ * the the terget element.
+ *
+ * You need Cache Killer on Chrome to see the effect on any FOM transformation
+ * directly on refresh (F5)
+ * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
+ */
+ function addAttributeInElement (node, elementTarget, separator) {
+ var mardownClassesInElementsRegex = new RegExp(separator, 'mg')
+ var mardownClassRegex = new RegExp('([^"= ]+?)="([^"=]+?)"', 'mg')
+ var nodeValue = node.nodeValue
+ var matches
+ var matchesClass
+ if ((matches = mardownClassesInElementsRegex.exec(nodeValue))) {
+ var classes = matches[1]
+ nodeValue = nodeValue.substring(0, matches.index) + nodeValue.substring(mardownClassesInElementsRegex.lastIndex)
+ node.nodeValue = nodeValue
+ while ((matchesClass = mardownClassRegex.exec(classes))) {
+ var name = matchesClass[1]
+ var value = matchesClass[2]
+ if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) { elementTarget.setAttribute(name, window.filterXSS.escapeAttrValue(value)) }
+ }
+ return true
+ }
+ return false
+ }
+
+ /**
+ * Add attributes to the parent element of a text node,
+ * or the element of an attribute node.
+ */
+ function addAttributes (section, element, previousElement, separatorElementAttributes, separatorSectionAttributes) {
+ if (element != null && element.childNodes !== undefined && element.childNodes.length > 0) {
+ var previousParentElement = element
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var childElement = element.childNodes[i]
+ if (i > 0) {
+ let j = i - 1
+ while (j >= 0) {
+ var aPreviousChildElement = element.childNodes[j]
+ if (typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== 'BR') {
+ previousParentElement = aPreviousChildElement
+ break
+ }
+ j = j - 1
+ }
+ }
+ var parentSection = section
+ if (childElement.nodeName === 'section') {
+ parentSection = childElement
+ previousParentElement = childElement
+ }
+ if (typeof childElement.setAttribute === 'function' || childElement.nodeType === Node.COMMENT_NODE) {
+ addAttributes(parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes)
+ }
+ }
+ }
+
+ if (element.nodeType === Node.COMMENT_NODE) {
+ if (addAttributeInElement(element, previousElement, separatorElementAttributes) === false) {
+ addAttributeInElement(element, section, separatorSectionAttributes)
+ }
+ }
+ }
+
+ /**
+ * Converts any current data-markdown slides in the
+ * DOM to HTML.
+ */
+ function convertSlides () {
+ var sections = document.querySelectorAll('[data-markdown]')
+
+ for (var i = 0, len = sections.length; i < len; i++) {
+ var section = sections[i]
+
+ // Only parse the same slide once
+ if (!section.getAttribute('data-markdown-parsed')) {
+ section.setAttribute('data-markdown-parsed', true)
+
+ var notes = section.querySelector('aside.notes')
+ var markdown = getMarkdownFromSlide(section)
+
+ var rendered = md.render(markdown)
+ rendered = preventXSS(rendered)
+ var result = window.postProcess(rendered)
+ section.innerHTML = result[0].outerHTML
+ addAttributes(section, section, null, section.getAttribute('data-element-attributes') ||
+ section.parentNode.getAttribute('data-element-attributes') ||
+ DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
+ section.getAttribute('data-attributes') ||
+ section.parentNode.getAttribute('data-attributes') ||
+ DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR)
+
+ // If there were notes, we need to re-add them after
+ // having overwritten the section's HTML
+ if (notes) {
+ section.appendChild(notes)
+ }
+ }
+ }
+ }
+
+ // API
+ return {
+ initialize: function () {
+ processSlides()
+ convertSlides()
+ },
+ // TODO: Do these belong in the API?
+ processSlides: processSlides,
+ convertSlides: convertSlides,
+ slidify: slidify
+ }
+}))
diff --git a/public/js/slide.js b/public/js/slide.js
index 63cf64c6..e743bb55 100644
--- a/public/js/slide.js
+++ b/public/js/slide.js
@@ -1,138 +1,139 @@
-require('../css/extra.css');
-require('../css/site.css');
+/* eslint-env browser, jquery */
+/* global serverurl, Reveal */
-import { md, updateLastChange, finishView } from './extra';
+require('../css/extra.css')
+require('../css/site.css')
-import { preventXSS } from './render';
+import { md, updateLastChange, finishView } from './extra'
-const body = $(".slides").text();
+const body = $('.slides').text()
-createtime = lastchangeui.time.attr('data-createtime');
-lastchangetime = lastchangeui.time.attr('data-updatetime');
-updateLastChange();
-const url = window.location.pathname;
-$('.ui-edit').attr('href', `${url}/edit`);
+window.createtime = window.lastchangeui.time.attr('data-createtime')
+window.lastchangetime = window.lastchangeui.time.attr('data-updatetime')
+updateLastChange()
+const url = window.location.pathname
+$('.ui-edit').attr('href', `${url}/edit`)
$(document).ready(() => {
- //tooltip
- $('[data-toggle="tooltip"]').tooltip();
-});
-
-function extend() {
- const target = {};
-
- for (const source of arguments) {
- for (const key in source) {
- if (source.hasOwnProperty(key)) {
- target[key] = source[key];
- }
- }
+ // tooltip
+ $('[data-toggle="tooltip"]').tooltip()
+})
+
+function extend () {
+ const target = {}
+
+ for (const source of arguments) {
+ for (const key in source) {
+ if (source.hasOwnProperty(key)) {
+ target[key] = source[key]
+ }
}
+ }
- return target;
+ return target
}
// Optional libraries used to extend on reveal.js
const deps = [{
- src: `${serverurl}/build/reveal.js/lib/js/classList.js`,
- condition() {
- return !document.body.classList;
- }
+ src: `${serverurl}/build/reveal.js/lib/js/classList.js`,
+ condition () {
+ return !document.body.classList
+ }
}, {
- src: `${serverurl}/js/reveal-markdown.js`,
- callback() {
- const slideOptions = {
- separator: '^(\r\n?|\n)---(\r\n?|\n)$',
- verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
- };
- const slides = RevealMarkdown.slidify(body, slideOptions);
- $(".slides").html(slides);
- RevealMarkdown.initialize();
- $(".slides").show();
+ src: `${serverurl}/js/reveal-markdown.js`,
+ callback () {
+ const slideOptions = {
+ separator: '^(\r\n?|\n)---(\r\n?|\n)$',
+ verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
}
+ const slides = window.RevealMarkdown.slidify(body, slideOptions)
+ $('.slides').html(slides)
+ window.RevealMarkdown.initialize()
+ $('.slides').show()
+ }
}, {
- src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`,
- async: true,
- condition() {
- return !!document.body.classList;
- }
-}];
+ src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`,
+ async: true,
+ condition () {
+ return !!document.body.classList
+ }
+}]
// default options to init reveal.js
const defaultOptions = {
- controls: true,
- progress: true,
- slideNumber: true,
- history: true,
- center: true,
- transition: 'none',
- dependencies: deps
-};
+ controls: true,
+ progress: true,
+ slideNumber: true,
+ history: true,
+ center: true,
+ transition: 'none',
+ dependencies: deps
+}
// options from yaml meta
-const meta = JSON.parse($("#meta").text());
-var options = meta.slideOptions || {};
+const meta = JSON.parse($('#meta').text())
+var options = meta.slideOptions || {}
-const view = $('.reveal');
+const view = $('.reveal')
-//text language
-if (meta.lang && typeof meta.lang == "string") {
- view.attr('lang', meta.lang);
+// text language
+if (meta.lang && typeof meta.lang === 'string') {
+ view.attr('lang', meta.lang)
} else {
- view.removeAttr('lang');
+ view.removeAttr('lang')
}
-//text direction
-if (meta.dir && typeof meta.dir == "string" && meta.dir == "rtl") {
- options.rtl = true;
+// text direction
+if (meta.dir && typeof meta.dir === 'string' && meta.dir === 'rtl') {
+ options.rtl = true
} else {
- options.rtl = false;
+ options.rtl = false
}
-//breaks
+// breaks
if (typeof meta.breaks === 'boolean' && !meta.breaks) {
- md.options.breaks = false;
+ md.options.breaks = false
} else {
- md.options.breaks = true;
+ md.options.breaks = true
}
// options from URL query string
-const queryOptions = Reveal.getQueryHash() || {};
+const queryOptions = Reveal.getQueryHash() || {}
-var options = extend(defaultOptions, options, queryOptions);
-Reveal.initialize(options);
+options = extend(defaultOptions, options, queryOptions)
+Reveal.initialize(options)
window.viewAjaxCallback = () => {
- Reveal.layout();
-};
-
-function renderSlide(event) {
- if (window.location.search.match( /print-pdf/gi )) {
- const slides = $('.slides');
- var title = document.title;
- finishView(slides);
- document.title = title;
- Reveal.layout();
- } else {
- const markdown = $(event.currentSlide);
- if (!markdown.attr('data-rendered')) {
- var title = document.title;
- finishView(markdown);
- markdown.attr('data-rendered', 'true');
- document.title = title;
- Reveal.layout();
- }
+ Reveal.layout()
+}
+
+function renderSlide (event) {
+ if (window.location.search.match(/print-pdf/gi)) {
+ const slides = $('.slides')
+ let title = document.title
+ finishView(slides)
+ document.title = title
+ Reveal.layout()
+ } else {
+ const markdown = $(event.currentSlide)
+ if (!markdown.attr('data-rendered')) {
+ let title = document.title
+ finishView(markdown)
+ markdown.attr('data-rendered', 'true')
+ document.title = title
+ Reveal.layout()
}
+ }
}
Reveal.addEventListener('ready', event => {
- renderSlide(event);
- const markdown = $(event.currentSlide);
+ renderSlide(event)
+ const markdown = $(event.currentSlide)
// force browser redraw
- setTimeout(() => {
- markdown.hide().show(0);
- }, 0);
-});
-Reveal.addEventListener('slidechanged', renderSlide);
+ setTimeout(() => {
+ markdown.hide().show(0)
+ }, 0)
+})
+Reveal.addEventListener('slidechanged', renderSlide)
-const isMacLike = navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) ? true : false;
+const isMacLike = !!navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)
-if (!isMacLike) $('.container').addClass('hidescrollbar');
+if (!isMacLike) $('.container').addClass('hidescrollbar')
diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js
index c9693176..c227f83f 100644
--- a/public/js/syncscroll.js
+++ b/public/js/syncscroll.js
@@ -1,365 +1,367 @@
+/* eslint-env browser, jquery */
+/* global _ */
// Inject line numbers for sync scroll.
-import markdownitContainer from 'markdown-it-container';
+import markdownitContainer from 'markdown-it-container'
-import { md } from './extra';
+import { md } from './extra'
-function addPart(tokens, idx) {
- if (tokens[idx].map && tokens[idx].level === 0) {
- const startline = tokens[idx].map[0] + 1;
- const endline = tokens[idx].map[1];
- tokens[idx].attrJoin('class', 'part');
- tokens[idx].attrJoin('data-startline', startline);
- tokens[idx].attrJoin('data-endline', endline);
- }
+function addPart (tokens, idx) {
+ if (tokens[idx].map && tokens[idx].level === 0) {
+ const startline = tokens[idx].map[0] + 1
+ const endline = tokens[idx].map[1]
+ tokens[idx].attrJoin('class', 'part')
+ tokens[idx].attrJoin('data-startline', startline)
+ tokens[idx].attrJoin('data-endline', endline)
+ }
}
md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.bullet_list_open = function (tokens, idx, options, env, self) {
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- if (tokens[idx].map) {
- const startline = tokens[idx].map[0] + 1;
- const endline = tokens[idx].map[1];
- tokens[idx].attrJoin('data-startline', startline);
- tokens[idx].attrJoin('data-endline', endline);
- }
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ if (tokens[idx].map) {
+ const startline = tokens[idx].map[0] + 1
+ const endline = tokens[idx].map[1]
+ tokens[idx].attrJoin('data-startline', startline)
+ tokens[idx].attrJoin('data-endline', endline)
+ }
+ return self.renderToken(...arguments)
+}
md.renderer.rules.ordered_list_open = function (tokens, idx, options, env, self) {
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.paragraph_open = function (tokens, idx, options, env, self) {
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
- tokens[idx].attrJoin('class', 'raw');
- addPart(tokens, idx);
- return self.renderToken(...arguments);
-};
+ tokens[idx].attrJoin('class', 'raw')
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
+}
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
- const token = tokens[idx];
- const info = token.info ? md.utils.unescapeAll(token.info).trim() : '';
- let langName = '';
- let highlighted;
-
- if (info) {
- langName = info.split(/\s+/g)[0];
- if (/\!$/.test(info)) token.attrJoin('class', 'wrap');
- token.attrJoin('class', options.langPrefix + langName.replace(/\=$|\=\d+$|\=\+$|\!$|\=\!/, ''));
- token.attrJoin('class', 'hljs');
- token.attrJoin('class', 'raw');
- }
-
- if (options.highlight) {
- highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content);
- } else {
- highlighted = md.utils.escapeHtml(token.content);
- }
-
- if (highlighted.indexOf('<pre') === 0) {
- return `${highlighted}\n`;
- }
-
- if (tokens[idx].map && tokens[idx].level === 0) {
- const startline = tokens[idx].map[0] + 1;
- const endline = tokens[idx].map[1];
- return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`;
- }
-
- return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`;
-};
+ const token = tokens[idx]
+ const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
+ let langName = ''
+ let highlighted
+
+ if (info) {
+ langName = info.split(/\s+/g)[0]
+ if (/!$/.test(info)) token.attrJoin('class', 'wrap')
+ token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!/, ''))
+ token.attrJoin('class', 'hljs')
+ token.attrJoin('class', 'raw')
+ }
+
+ if (options.highlight) {
+ highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content)
+ } else {
+ highlighted = md.utils.escapeHtml(token.content)
+ }
+
+ if (highlighted.indexOf('<pre') === 0) {
+ return `${highlighted}\n`
+ }
+
+ if (tokens[idx].map && tokens[idx].level === 0) {
+ const startline = tokens[idx].map[0] + 1
+ const endline = tokens[idx].map[1]
+ return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`
+ }
+
+ return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`
+}
md.renderer.rules.code_block = (tokens, idx, options, env, self) => {
- if (tokens[idx].map && tokens[idx].level === 0) {
- const startline = tokens[idx].map[0] + 1;
- const endline = tokens[idx].map[1];
- return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`;
- }
- return `<pre><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`;
-};
-function renderContainer(tokens, idx, options, env, self) {
- tokens[idx].attrJoin('role', 'alert');
- tokens[idx].attrJoin('class', 'alert');
- tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`);
- addPart(tokens, idx);
- return self.renderToken(...arguments);
+ if (tokens[idx].map && tokens[idx].level === 0) {
+ const startline = tokens[idx].map[0] + 1
+ const endline = tokens[idx].map[1]
+ return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`
+ }
+ return `<pre><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`
+}
+function renderContainer (tokens, idx, options, env, self) {
+ tokens[idx].attrJoin('role', 'alert')
+ tokens[idx].attrJoin('class', 'alert')
+ tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`)
+ addPart(tokens, idx)
+ return self.renderToken(...arguments)
}
-md.use(markdownitContainer, 'success', { render: renderContainer });
-md.use(markdownitContainer, 'info', { render: renderContainer });
-md.use(markdownitContainer, 'warning', { render: renderContainer });
-md.use(markdownitContainer, 'danger', { render: renderContainer });
+md.use(markdownitContainer, 'success', { render: renderContainer })
+md.use(markdownitContainer, 'info', { render: renderContainer })
+md.use(markdownitContainer, 'warning', { render: renderContainer })
+md.use(markdownitContainer, 'danger', { render: renderContainer })
// FIXME: expose syncscroll to window
-window.syncscroll = true;
+window.syncscroll = true
-window.preventSyncScrollToEdit = false;
-window.preventSyncScrollToView = false;
+window.preventSyncScrollToEdit = false
+window.preventSyncScrollToView = false
-const editScrollThrottle = 5;
-const viewScrollThrottle = 5;
-const buildMapThrottle = 100;
+const editScrollThrottle = 5
+const viewScrollThrottle = 5
+const buildMapThrottle = 100
-let viewScrolling = false;
-let editScrolling = false;
+let viewScrolling = false
+let editScrolling = false
-let editArea = null;
-let viewArea = null;
-let markdownArea = null;
+let editArea = null
+let viewArea = null
+let markdownArea = null
-export function setupSyncAreas(edit, view, markdown) {
- editArea = edit;
- viewArea = view;
- markdownArea = markdown;
- editArea.on('scroll', _.throttle(syncScrollToView, editScrollThrottle));
- viewArea.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle));
+export function setupSyncAreas (edit, view, markdown) {
+ editArea = edit
+ viewArea = view
+ markdownArea = markdown
+ editArea.on('scroll', _.throttle(syncScrollToView, editScrollThrottle))
+ viewArea.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle))
}
-let scrollMap, lineHeightMap, viewTop, viewBottom;
+let scrollMap, lineHeightMap, viewTop, viewBottom
-export function clearMap() {
- scrollMap = null;
- lineHeightMap = null;
- viewTop = null;
- viewBottom = null;
+export function clearMap () {
+ scrollMap = null
+ lineHeightMap = null
+ viewTop = null
+ viewBottom = null
}
-window.viewAjaxCallback = clearMap;
+window.viewAjaxCallback = clearMap
-const buildMap = _.throttle(buildMapInner, buildMapThrottle);
+const buildMap = _.throttle(buildMapInner, buildMapThrottle)
// 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 buildMapInner(callback) {
- if (!viewArea || !markdownArea) return;
- let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap;
-
- offset = viewArea.scrollTop() - viewArea.offset().top;
- _scrollMap = [];
- nonEmptyList = [];
- _lineHeightMap = [];
- viewTop = 0;
- viewBottom = viewArea[0].scrollHeight - viewArea.height();
-
- acc = 0;
- const lines = editor.getValue().split('\n');
- const lineHeight = editor.defaultTextHeight();
- for (i = 0; i < lines.length; i++) {
- const str = lines[i];
-
- _lineHeightMap.push(acc);
-
- if (str.length === 0) {
- acc++;
- continue;
- }
-
- const h = editor.heightAtLine(i + 1) - editor.heightAtLine(i);
- acc += Math.round(h / lineHeight);
+function buildMapInner (callback) {
+ if (!viewArea || !markdownArea) return
+ let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap
+
+ offset = viewArea.scrollTop() - viewArea.offset().top
+ _scrollMap = []
+ nonEmptyList = []
+ _lineHeightMap = []
+ viewTop = 0
+ viewBottom = viewArea[0].scrollHeight - viewArea.height()
+
+ acc = 0
+ const lines = window.editor.getValue().split('\n')
+ const lineHeight = window.editor.defaultTextHeight()
+ for (i = 0; i < lines.length; i++) {
+ const str = lines[i]
+
+ _lineHeightMap.push(acc)
+
+ if (str.length === 0) {
+ acc++
+ continue
}
- _lineHeightMap.push(acc);
- linesCount = acc;
- for (i = 0; i < linesCount; i++) {
- _scrollMap.push(-1);
- }
+ const h = window.editor.heightAtLine(i + 1) - window.editor.heightAtLine(i)
+ acc += Math.round(h / lineHeight)
+ }
+ _lineHeightMap.push(acc)
+ linesCount = acc
+
+ for (i = 0; i < linesCount; i++) {
+ _scrollMap.push(-1)
+ }
- nonEmptyList.push(0);
+ nonEmptyList.push(0)
// make the first line go top
- _scrollMap[0] = viewTop;
-
- const parts = markdownArea.find('.part').toArray();
- for (i = 0; i < parts.length; i++) {
- const $el = $(parts[i]);
- let t = $el.attr('data-startline') - 1;
- if (t === '') {
- return;
- }
- t = _lineHeightMap[t];
- if (t !== 0 && t !== nonEmptyList[nonEmptyList.length - 1]) {
- nonEmptyList.push(t);
- }
- _scrollMap[t] = Math.round($el.offset().top + offset - 10);
+ _scrollMap[0] = viewTop
+
+ const parts = markdownArea.find('.part').toArray()
+ for (i = 0; i < parts.length; i++) {
+ const $el = $(parts[i])
+ let t = $el.attr('data-startline') - 1
+ if (t === '') {
+ return
}
+ t = _lineHeightMap[t]
+ if (t !== 0 && t !== nonEmptyList[nonEmptyList.length - 1]) {
+ nonEmptyList.push(t)
+ }
+ _scrollMap[t] = Math.round($el.offset().top + offset - 10)
+ }
- nonEmptyList.push(linesCount);
- _scrollMap[linesCount] = viewArea[0].scrollHeight;
-
- pos = 0;
- for (i = 1; i < linesCount; i++) {
- if (_scrollMap[i] !== -1) {
- pos++;
- continue;
- }
+ nonEmptyList.push(linesCount)
+ _scrollMap[linesCount] = viewArea[0].scrollHeight
- a = nonEmptyList[pos];
- b = nonEmptyList[pos + 1];
- _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a));
+ pos = 0
+ for (i = 1; i < linesCount; i++) {
+ if (_scrollMap[i] !== -1) {
+ pos++
+ continue
}
- _scrollMap[0] = 0;
+ a = nonEmptyList[pos]
+ b = nonEmptyList[pos + 1]
+ _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a))
+ }
+
+ _scrollMap[0] = 0
- scrollMap = _scrollMap;
- lineHeightMap = _lineHeightMap;
+ scrollMap = _scrollMap
+ lineHeightMap = _lineHeightMap
- if (loaded && callback) callback();
+ if (window.loaded && callback) callback()
}
// sync view scroll progress to edit
-let viewScrollingTimer = null;
-
-export function syncScrollToEdit(event, preventAnimate) {
- if (currentMode != modeType.both || !syncscroll || !editArea) return;
- if (preventSyncScrollToEdit) {
- if (typeof preventSyncScrollToEdit === 'number') {
- preventSyncScrollToEdit--;
- } else {
- preventSyncScrollToEdit = false;
- }
- return;
- }
- if (!scrollMap || !lineHeightMap) {
- buildMap(() => {
- syncScrollToEdit(event, preventAnimate);
- });
- return;
- }
- if (editScrolling) return;
-
- const scrollTop = viewArea[0].scrollTop;
- let lineIndex = 0;
- for (var i = 0, l = scrollMap.length; i < l; i++) {
- if (scrollMap[i] > scrollTop) {
- break;
- } else {
- lineIndex = i;
- }
- }
- let lineNo = 0;
- let lineDiff = 0;
- for (var i = 0, l = lineHeightMap.length; i < l; i++) {
- if (lineHeightMap[i] > lineIndex) {
- break;
- } else {
- lineNo = lineHeightMap[i];
- lineDiff = lineHeightMap[i + 1] - lineNo;
- }
- }
+let viewScrollingTimer = null
- let posTo = 0;
- let topDiffPercent = 0;
- let posToNextDiff = 0;
- const scrollInfo = editor.getScrollInfo();
- const textHeight = editor.defaultTextHeight();
- const preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight;
- const preLastLineNo = Math.round(preLastLineHeight / textHeight);
- const preLastLinePos = scrollMap[preLastLineNo];
-
- if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) {
- posTo = preLastLineHeight;
- topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos);
- posToNextDiff = textHeight * topDiffPercent;
- posTo += Math.ceil(posToNextDiff);
+export function syncScrollToEdit (event, preventAnimate) {
+ if (window.currentMode !== window.modeType.both || !window.syncscroll || !editArea) return
+ if (window.preventSyncScrollToEdit) {
+ if (typeof window.preventSyncScrollToEdit === 'number') {
+ window.preventSyncScrollToEdit--
} else {
- posTo = lineNo * textHeight;
- topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]);
- posToNextDiff = textHeight * lineDiff * topDiffPercent;
- posTo += Math.ceil(posToNextDiff);
+ window.preventSyncScrollToEdit = false
}
-
- if (preventAnimate) {
- editArea.scrollTop(posTo);
+ return
+ }
+ if (!scrollMap || !lineHeightMap) {
+ buildMap(() => {
+ syncScrollToEdit(event, preventAnimate)
+ })
+ return
+ }
+ if (editScrolling) return
+
+ const scrollTop = viewArea[0].scrollTop
+ let lineIndex = 0
+ for (let i = 0, l = scrollMap.length; i < l; i++) {
+ if (scrollMap[i] > scrollTop) {
+ break
} else {
- const posDiff = Math.abs(scrollInfo.top - posTo);
- var duration = posDiff / 50;
- duration = duration >= 100 ? duration : 100;
- editArea.stop(true, true).animate({
- scrollTop: posTo
- }, duration, "linear");
+ lineIndex = i
}
-
- viewScrolling = true;
- clearTimeout(viewScrollingTimer);
- viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5);
+ }
+ let lineNo = 0
+ let lineDiff = 0
+ for (let i = 0, l = lineHeightMap.length; i < l; i++) {
+ if (lineHeightMap[i] > lineIndex) {
+ break
+ } else {
+ lineNo = lineHeightMap[i]
+ lineDiff = lineHeightMap[i + 1] - lineNo
+ }
+ }
+
+ let posTo = 0
+ let topDiffPercent = 0
+ let posToNextDiff = 0
+ const scrollInfo = window.editor.getScrollInfo()
+ const textHeight = window.editor.defaultTextHeight()
+ const preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight
+ const preLastLineNo = Math.round(preLastLineHeight / textHeight)
+ const preLastLinePos = scrollMap[preLastLineNo]
+
+ if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) {
+ posTo = preLastLineHeight
+ topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos)
+ posToNextDiff = textHeight * topDiffPercent
+ posTo += Math.ceil(posToNextDiff)
+ } else {
+ posTo = lineNo * textHeight
+ topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo])
+ posToNextDiff = textHeight * lineDiff * topDiffPercent
+ posTo += Math.ceil(posToNextDiff)
+ }
+
+ if (preventAnimate) {
+ editArea.scrollTop(posTo)
+ } else {
+ const posDiff = Math.abs(scrollInfo.top - posTo)
+ var duration = posDiff / 50
+ duration = duration >= 100 ? duration : 100
+ editArea.stop(true, true).animate({
+ scrollTop: posTo
+ }, duration, 'linear')
+ }
+
+ viewScrolling = true
+ clearTimeout(viewScrollingTimer)
+ viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5)
}
-function viewScrollingTimeoutInner() {
- viewScrolling = false;
+function viewScrollingTimeoutInner () {
+ viewScrolling = false
}
// sync edit scroll progress to view
-let editScrollingTimer = null;
-
-export function syncScrollToView(event, preventAnimate) {
- if (currentMode != modeType.both || !syncscroll || !viewArea) return;
- if (preventSyncScrollToView) {
- if (typeof preventSyncScrollToView === 'number') {
- preventSyncScrollToView--;
- } else {
- preventSyncScrollToView = false;
- }
- return;
- }
- if (!scrollMap || !lineHeightMap) {
- buildMap(() => {
- syncScrollToView(event, preventAnimate);
- });
- return;
- }
- if (viewScrolling) return;
-
- let lineNo, posTo;
- let topDiffPercent, posToNextDiff;
- const scrollInfo = editor.getScrollInfo();
- const textHeight = editor.defaultTextHeight();
- lineNo = Math.floor(scrollInfo.top / textHeight);
- // if reach the last line, will start lerp to the bottom
- const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight);
- if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
- topDiffPercent = diffToBottom / textHeight;
- posTo = scrollMap[lineNo + 1];
- posToNextDiff = (viewBottom - posTo) * topDiffPercent;
- posTo += Math.floor(posToNextDiff);
- } else {
- topDiffPercent = (scrollInfo.top % textHeight) / textHeight;
- posTo = scrollMap[lineNo];
- posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent;
- posTo += Math.floor(posToNextDiff);
- }
+let editScrollingTimer = null
- if (preventAnimate) {
- viewArea.scrollTop(posTo);
+export function syncScrollToView (event, preventAnimate) {
+ if (window.currentMode !== window.modeType.both || !window.syncscroll || !viewArea) return
+ if (window.preventSyncScrollToView) {
+ if (typeof preventSyncScrollToView === 'number') {
+ window.preventSyncScrollToView--
} else {
- const posDiff = Math.abs(viewArea.scrollTop() - posTo);
- var duration = posDiff / 50;
- duration = duration >= 100 ? duration : 100;
- viewArea.stop(true, true).animate({
- scrollTop: posTo
- }, duration, "linear");
+ window.preventSyncScrollToView = false
}
-
- editScrolling = true;
- clearTimeout(editScrollingTimer);
- editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5);
+ return
+ }
+ if (!scrollMap || !lineHeightMap) {
+ buildMap(() => {
+ syncScrollToView(event, preventAnimate)
+ })
+ return
+ }
+ if (viewScrolling) return
+
+ let lineNo, posTo
+ let topDiffPercent, posToNextDiff
+ const scrollInfo = window.editor.getScrollInfo()
+ const textHeight = window.editor.defaultTextHeight()
+ lineNo = Math.floor(scrollInfo.top / textHeight)
+ // if reach the last line, will start lerp to the bottom
+ const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight)
+ if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
+ topDiffPercent = diffToBottom / textHeight
+ posTo = scrollMap[lineNo + 1]
+ posToNextDiff = (viewBottom - posTo) * topDiffPercent
+ posTo += Math.floor(posToNextDiff)
+ } else {
+ topDiffPercent = (scrollInfo.top % textHeight) / textHeight
+ posTo = scrollMap[lineNo]
+ posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent
+ posTo += Math.floor(posToNextDiff)
+ }
+
+ if (preventAnimate) {
+ viewArea.scrollTop(posTo)
+ } else {
+ const posDiff = Math.abs(viewArea.scrollTop() - posTo)
+ var duration = posDiff / 50
+ duration = duration >= 100 ? duration : 100
+ viewArea.stop(true, true).animate({
+ scrollTop: posTo
+ }, duration, 'linear')
+ }
+
+ editScrolling = true
+ clearTimeout(editScrollingTimer)
+ editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5)
}
-function editScrollingTimeoutInner() {
- editScrolling = false;
+function editScrollingTimeoutInner () {
+ editScrolling = false
}
diff --git a/public/vendor/md-toc.js b/public/vendor/md-toc.js
index 200275a5..f93f7921 100755
--- a/public/vendor/md-toc.js
+++ b/public/vendor/md-toc.js
@@ -1,129 +1,123 @@
+/* eslint-env browser, jquery */
/**
* md-toc.js v1.0.2
* https://github.com/yijian166/md-toc.js
*/
(function (window) {
- function Toc(id, options) {
- this.el = document.getElementById(id);
- if (!this.el) return;
- this.options = options || {};
- this.tocLevel = parseInt(options.level) || 0;
- this.tocClass = options['class'] || 'toc';
- this.ulClass = options['ulClass'];
- this.tocTop = parseInt(options.top) || 0;
- this.elChilds = this.el.children;
- this.process = options['process'];
- if (!this.elChilds.length) return;
- this._init();
- }
+ function Toc (id, options) {
+ this.el = document.getElementById(id)
+ if (!this.el) return
+ this.options = options || {}
+ this.tocLevel = parseInt(options.level) || 0
+ this.tocClass = options['class'] || 'toc'
+ this.ulClass = options['ulClass']
+ this.tocTop = parseInt(options.top) || 0
+ this.elChilds = this.el.children
+ this.process = options['process']
+ if (!this.elChilds.length) return
+ this._init()
+ }
- Toc.prototype._init = function () {
- this._collectTitleElements();
- this._createTocContent();
- this._showToc();
- };
+ Toc.prototype._init = function () {
+ this._collectTitleElements()
+ this._createTocContent()
+ this._showToc()
+ }
- Toc.prototype._collectTitleElements = function () {
- this._elTitlesNames = [],
- this.elTitleElements = [];
- for (var i = 1; i < 7; i++) {
- if (this.el.getElementsByTagName('h' + i).length) {
- this._elTitlesNames.push('h' + i);
- }
- }
+ Toc.prototype._collectTitleElements = function () {
+ this._elTitlesNames = []
+ this.elTitleElements = []
+ for (var i = 1; i < 7; i++) {
+ if (this.el.getElementsByTagName('h' + i).length) {
+ this._elTitlesNames.push('h' + i)
+ }
+ }
- this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length;
+ this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length
- for (var j = 0; j < this.elChilds.length; j++) {
- this._elChildName = this.elChilds[j].tagName.toLowerCase();
- if (this._elTitlesNames.toString().match(this._elChildName)) {
- this.elTitleElements.push(this.elChilds[j]);
- }
- }
- };
+ for (var j = 0; j < this.elChilds.length; j++) {
+ this._elChildName = this.elChilds[j].tagName.toLowerCase()
+ if (this._elTitlesNames.toString().match(this._elChildName)) {
+ this.elTitleElements.push(this.elChilds[j])
+ }
+ }
+ }
- Toc.prototype._createTocContent = function () {
- this._elTitleElementsLen = this.elTitleElements.length;
- if (!this._elTitleElementsLen) return;
- this.tocContent = '';
- this._tempLists = [];
+ Toc.prototype._createTocContent = function () {
+ this._elTitleElementsLen = this.elTitleElements.length
+ if (!this._elTitleElementsLen) return
+ this.tocContent = ''
+ this._tempLists = []
- var url = location.origin + location.pathname;
- for (var i = 0; i < this._elTitleElementsLen; i++) {
- var j = i + 1;
- this._elTitleElement = this.elTitleElements[i];
- this._elTitleElementName = this._elTitleElement.tagName;
- this._elTitleElementText = (typeof this.process === 'function' ? this.process(this._elTitleElement) : this._elTitleElement.innerHTML).replace(/<(?:.|\n)*?>/gm, '');
- var id = this._elTitleElement.getAttribute('id');
- if (!id) {
- this._elTitleElement.setAttribute('id', 'tip' + i);
- id = '#tip' + i;
- } else {
- id = '#' + id;
- }
+ for (var i = 0; i < this._elTitleElementsLen; i++) {
+ var j = i + 1
+ this._elTitleElement = this.elTitleElements[i]
+ this._elTitleElementName = this._elTitleElement.tagName
+ this._elTitleElementText = (typeof this.process === 'function' ? this.process(this._elTitleElement) : this._elTitleElement.innerHTML).replace(/<(?:.|\n)*?>/gm, '')
+ var id = this._elTitleElement.getAttribute('id')
+ if (!id) {
+ this._elTitleElement.setAttribute('id', 'tip' + i)
+ id = '#tip' + i
+ } else {
+ id = '#' + id
+ }
- this.tocContent += '<li><a href="' + id + '">' + this._elTitleElementText + '</a>';
+ this.tocContent += '<li><a href="' + id + '">' + this._elTitleElementText + '</a>'
- if (j != this._elTitleElementsLen) {
- this._elNextTitleElementName = this.elTitleElements[j].tagName;
- if (this._elTitleElementName != this._elNextTitleElementName) {
- var checkColse = false,
- y = 1;
- for (var t = this._tempLists.length - 1; t >= 0; t--) {
- if (this._tempLists[t].tagName == this._elNextTitleElementName) {
- checkColse = true;
- break;
- }
- y++;
- }
- if (checkColse) {
- this.tocContent += new Array(y + 1).join('</li></ul>');
- this._tempLists.length = this._tempLists.length - y;
- } else {
- this._tempLists.push(this._elTitleElement);
- if (this.ulClass)
- this.tocContent += '<ul class="' + this.ulClass + '">';
- else
- this.tocContent += '<ul>';
- }
- } else {
- this.tocContent += '</li>';
- }
- } else {
- if (this._tempLists.length) {
- this.tocContent += new Array(this._tempLists.length + 1).join('</li></ul>');
- } else {
- this.tocContent += '</li>';
- }
+ if (j !== this._elTitleElementsLen) {
+ this._elNextTitleElementName = this.elTitleElements[j].tagName
+ if (this._elTitleElementName !== this._elNextTitleElementName) {
+ var checkColse = false
+ var y = 1
+ for (var t = this._tempLists.length - 1; t >= 0; t--) {
+ if (this._tempLists[t].tagName === this._elNextTitleElementName) {
+ checkColse = true
+ break
}
+ y++
+ }
+ if (checkColse) {
+ this.tocContent += new Array(y + 1).join('</li></ul>')
+ this._tempLists.length = this._tempLists.length - y
+ } else {
+ this._tempLists.push(this._elTitleElement)
+ if (this.ulClass) { this.tocContent += '<ul class="' + this.ulClass + '">' } else { this.tocContent += '<ul>' }
+ }
+ } else {
+ this.tocContent += '</li>'
}
- if (this.ulClass)
- this.tocContent = '<ul class="' + this.ulClass + '">' + this.tocContent + '</ul>';
- else
- this.tocContent = '<ul>' + this.tocContent + '</ul>';
- };
-
- Toc.prototype._showToc = function () {
- this.toc = document.createElement('div');
- this.toc.innerHTML = this.tocContent;
- this.toc.setAttribute('class', this.tocClass);
- if (!this.options.targetId) {
- this.el.appendChild(this.toc);
+ } else {
+ if (this._tempLists.length) {
+ this.tocContent += new Array(this._tempLists.length + 1).join('</li></ul>')
} else {
- document.getElementById(this.options.targetId).appendChild(this.toc);
+ this.tocContent += '</li>'
}
- var self = this;
- if (this.tocTop > -1) {
- window.onscroll = function () {
- var t = document.documentElement.scrollTop || document.body.scrollTop;
- if (t < self.tocTop) {
- self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;');
- } else {
- self.toc.setAttribute('style', 'position:fixed;top:10px;');
- }
- }
+ }
+ }
+ if (this.ulClass) { this.tocContent = '<ul class="' + this.ulClass + '">' + this.tocContent + '</ul>' } else { this.tocContent = '<ul>' + this.tocContent + '</ul>' }
+ }
+
+ Toc.prototype._showToc = function () {
+ this.toc = document.createElement('div')
+ this.toc.innerHTML = this.tocContent
+ this.toc.setAttribute('class', this.tocClass)
+ if (!this.options.targetId) {
+ this.el.appendChild(this.toc)
+ } else {
+ document.getElementById(this.options.targetId).appendChild(this.toc)
+ }
+ var self = this
+ if (this.tocTop > -1) {
+ window.onscroll = function () {
+ var t = document.documentElement.scrollTop || document.body.scrollTop
+ if (t < self.tocTop) {
+ self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;')
+ } else {
+ self.toc.setAttribute('style', 'position:fixed;top:10px;')
}
- };
- window.Toc = Toc;
-})(window); \ No newline at end of file
+ }
+ }
+ }
+ window.Toc = Toc
+})(window)