', {
class: 'authorship-gutter ' + className,
title: author.name
})
@@ -1985,8 +1986,8 @@ function iterateLine (line) {
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]
+ for (let i = 0; i < currMark.textmarkers.length; i++) {
+ const textMarker = currMark.textmarkers[i]
if (textMarker.userid !== currMark.gutter.userid) {
addTextMarkers.push(textMarker)
}
@@ -1997,12 +1998,12 @@ editorInstance.on('update', function () {
$('.authorship-gutter:not([data-original-title])').tooltip({
container: '.CodeMirror-lines',
placement: 'right',
- delay: { 'show': 500, 'hide': 100 }
+ delay: { show: 500, hide: 100 }
})
$('.authorship-inline:not([data-original-title])').tooltip({
container: '.CodeMirror-lines',
placement: 'bottom',
- delay: { 'show': 500, 'hide': 100 }
+ delay: { show: 500, hide: 100 }
})
// clear tooltip which described element has been removed
$('[id^="tooltip"]').each(function (index, element) {
@@ -2063,7 +2064,7 @@ var cmClient = null
var synchronized_ = null
function havePendingOperation () {
- return !!((cmClient && cmClient.state && cmClient.state.hasOwnProperty('outstanding')))
+ return !!((cmClient && cmClient.state && Object.hasOwnProperty.call(cmClient.state, 'outstanding')))
}
socket.on('doc', function (obj) {
@@ -2223,7 +2224,7 @@ function updateOnlineStatus () {
break
}
}
- let id = items[i].values().id
+ const id = items[i].values().id
if (found) {
onlineUserList.get('id', id)[0].values(_onlineUsers[foundindex])
shortOnlineUserList.get('id', id)[0].values(_onlineUsers[foundindex])
@@ -2417,19 +2418,19 @@ function buildCursor (user) {
break
}
if ($('div[data-clientid="' + user.id + '"]').length <= 0) {
- let cursor = $('
')
+ const cursor = $('
')
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 = $('
')
+ const cursorbar = $('
')
cursorbar[0].style.height = defaultTextHeight + 'px'
cursorbar[0].style.borderLeft = '2px solid ' + user.color
var icon = '
'
- let cursortag = $('
' + icon + ' ' + user.name + '
')
+ const cursortag = $('
' + icon + ' ' + user.name + '
')
// cursortag[0].style.background = color;
cursortag[0].style.color = user.color
@@ -2485,15 +2486,15 @@ function buildCursor (user) {
checkCursorTag(coord, cursortag)
} else {
- let cursor = $('div[data-clientid="' + user.id + '"]')
+ const 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')
+ const cursorbar = cursor.find('.cursorbar')
cursorbar[0].style.height = defaultTextHeight + 'px'
cursorbar[0].style.borderLeft = '2px solid ' + user.color
- let cursortag = cursor.find('.cursortag')
+ const cursortag = cursor.find('.cursortag')
cursortag.find('i').removeClass().addClass('fa').addClass(iconClass)
cursortag.find('.name').text(user.name)
@@ -2502,8 +2503,8 @@ function buildCursor (user) {
cursor[0].style.top = coord.top + 'px'
} else {
cursor.animate({
- 'left': coord.left,
- 'top': coord.top
+ left: coord.left,
+ top: coord.top
}, {
duration: cursorAnimatePeriod,
queue: false
@@ -2712,8 +2713,8 @@ function restoreInfo () {
$(window).scrollLeft(lastInfo.edit.scroll.left)
$(window).scrollTop(lastInfo.edit.scroll.top)
} else {
- let left = lastInfo.edit.scroll.left
- let top = lastInfo.edit.scroll.top
+ const left = lastInfo.edit.scroll.left
+ const top = lastInfo.edit.scroll.top
editor.scrollIntoView()
editor.scrollTo(left, top)
}
@@ -2723,8 +2724,8 @@ function restoreInfo () {
$(window).scrollTop(lastInfo.view.scroll.top)
break
case modeType.both:
- let left = lastInfo.edit.scroll.left
- let top = lastInfo.edit.scroll.top
+ const left = lastInfo.edit.scroll.left
+ const top = lastInfo.edit.scroll.top
editor.scrollIntoView()
editor.scrollTo(left, top)
ui.area.view.scrollLeft(lastInfo.view.scroll.left)
@@ -2846,8 +2847,8 @@ function partialUpdate (src, tar, des) {
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])
+ const rawSrc = cloneAndRemoveDataAttr(src[i])
+ const rawTar = cloneAndRemoveDataAttr(tar[i])
if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
start = i
break
@@ -2859,8 +2860,8 @@ function partialUpdate (src, tar, des) {
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])
+ const rawSrc = cloneAndRemoveDataAttr(src[i])
+ const rawTar = cloneAndRemoveDataAttr(tar[i])
if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
start = i
break
@@ -2868,12 +2869,12 @@ function partialUpdate (src, tar, des) {
}
// tar end
for (let i = 1; i <= tar.length + 1; i++) {
- let srcLength = src.length
- let tarLength = tar.length
+ const srcLength = src.length
+ const 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])
+ const rawSrc = cloneAndRemoveDataAttr(src[srcLength - i])
+ const rawTar = cloneAndRemoveDataAttr(tar[tarLength - i])
if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
tarEnd = tar.length - i
break
@@ -2881,12 +2882,12 @@ function partialUpdate (src, tar, des) {
}
// src end
for (let i = 1; i <= src.length + 1; i++) {
- let srcLength = src.length
- let tarLength = tar.length
+ const srcLength = src.length
+ const 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])
+ const rawSrc = cloneAndRemoveDataAttr(src[srcLength - i])
+ const rawTar = cloneAndRemoveDataAttr(tar[tarLength - i])
if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) {
srcEnd = src.length - i
break
@@ -3115,6 +3116,27 @@ function matchInContainer (text) {
}
}
+const textCompleteKeyMap = {
+ 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')
+ }
+}
+
$(editor.getInputField())
.textcomplete([
{ // emoji strategy
@@ -3316,29 +3338,10 @@ $(editor.getInputField())
},
'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')
- }
- })
+ editor.addKeyMap(textCompleteKeyMap)
},
'textComplete:hide': function (e) {
$(this).data('autocompleting', false)
- editor.setOption('extraKeys', editorInstance.defaultExtraKeys)
+ editor.removeKeyMap(textCompleteKeyMap)
}
})
diff --git a/public/js/lib/appState.js b/public/js/lib/appState.js
index 87aaf737..f409908c 100644
--- a/public/js/lib/appState.js
+++ b/public/js/lib/appState.js
@@ -1,6 +1,6 @@
import modeType from './modeType'
-let state = {
+const state = {
syncscroll: true,
currentMode: modeType.view,
nightMode: false
diff --git a/public/js/lib/common/constant.ejs b/public/js/lib/common/constant.ejs
index a94b815e..537985f4 100644
--- a/public/js/lib/common/constant.ejs
+++ b/public/js/lib/common/constant.ejs
@@ -2,7 +2,12 @@ window.domain = '<%- domain %>'
window.urlpath = '<%- urlpath %>'
window.debug = <%- debug %>
window.version = '<%- version %>'
+window.plantumlServer = '<%- plantumlServer %>'
window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
+window.defaultUseHardbreak = <%- defaultUseHardbreak %>
+
+window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'
+
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
diff --git a/public/js/lib/editor/config.js b/public/js/lib/editor/config.js
index 9508b847..338ef6dc 100644
--- a/public/js/lib/editor/config.js
+++ b/public/js/lib/editor/config.js
@@ -1,4 +1,4 @@
-let config = {
+const config = {
docmaxlength: null
}
diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js
index f05d01b8..4b30a130 100644
--- a/public/js/lib/editor/index.js
+++ b/public/js/lib/editor/index.js
@@ -1,7 +1,11 @@
+/* global CodeMirror, $, editor, Cookies */
import * as utils from './utils'
import config from './config'
import statusBarTemplate from './statusbar.html'
import toolBarTemplate from './toolbar.html'
+import './markdown-lint'
+import { initTableEditor } from './table-editor'
+import { options, Alignment, FormatType } from '@susisu/mte-kernel'
/* config section */
const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault
@@ -75,8 +79,8 @@ export default class Editor {
},
'Cmd-Left': 'goLineLeftSmart',
'Cmd-Right': 'goLineRight',
- 'Home': 'goLineLeftSmart',
- 'End': 'goLineRight',
+ Home: 'goLineLeftSmart',
+ End: 'goLineRight',
'Ctrl-C': function (cm) {
if (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') {
document.execCommand('copy')
@@ -158,6 +162,19 @@ export default class Editor {
var makeLine = $('#makeLine')
var makeComment = $('#makeComment')
+ var insertRow = $('#insertRow')
+ var deleteRow = $('#deleteRow')
+ var moveRowUp = $('#moveRowUp')
+ var moveRowDown = $('#moveRowDown')
+ var insertColumn = $('#insertColumn')
+ var deleteColumn = $('#deleteColumn')
+ var moveColumnLeft = $('#moveColumnLeft')
+ var moveColumnRight = $('#moveColumnRight')
+ var alignLeft = $('#alignLeft')
+ var alignCenter = $('#alignCenter')
+ var alignRight = $('#alignRight')
+ var alignNone = $('#alignNone')
+
makeBold.click(() => {
utils.wrapTextWith(this.editor, this.editor, '**')
this.editor.focus()
@@ -217,6 +234,72 @@ export default class Editor {
makeComment.click(() => {
utils.insertText(this.editor, '> []')
})
+
+ // table tools UI
+ const opts = options({
+ smartCursor: true,
+ formatType: FormatType.NORMAL
+ })
+
+ insertRow.click(() => {
+ this.tableEditor.insertRow(opts)
+ this.editor.focus()
+ })
+
+ deleteRow.click(() => {
+ this.tableEditor.deleteRow(opts)
+ this.editor.focus()
+ })
+
+ moveRowUp.click(() => {
+ this.tableEditor.moveRow(-1, opts)
+ this.editor.focus()
+ })
+
+ moveRowDown.click(() => {
+ this.tableEditor.moveRow(1, opts)
+ this.editor.focus()
+ })
+
+ insertColumn.click(() => {
+ this.tableEditor.insertColumn(opts)
+ this.editor.focus()
+ })
+
+ deleteColumn.click(() => {
+ this.tableEditor.deleteColumn(opts)
+ this.editor.focus()
+ })
+
+ moveColumnLeft.click(() => {
+ this.tableEditor.moveColumn(-1, opts)
+ this.editor.focus()
+ })
+
+ moveColumnRight.click(() => {
+ this.tableEditor.moveColumn(1, opts)
+ this.editor.focus()
+ })
+
+ alignLeft.click(() => {
+ this.tableEditor.alignColumn(Alignment.LEFT, opts)
+ this.editor.focus()
+ })
+
+ alignCenter.click(() => {
+ this.tableEditor.alignColumn(Alignment.CENTER, opts)
+ this.editor.focus()
+ })
+
+ alignRight.click(() => {
+ this.tableEditor.alignColumn(Alignment.RIGHT, opts)
+ this.editor.focus()
+ })
+
+ alignNone.click(() => {
+ this.tableEditor.alignColumn(Alignment.NONE, opts)
+ this.editor.focus()
+ })
}
addStatusBar () {
@@ -230,6 +313,7 @@ export default class Editor {
this.statusLength = this.statusBar.find('.status-length')
this.statusTheme = this.statusBar.find('.status-theme')
this.statusSpellcheck = this.statusBar.find('.status-spellcheck')
+ this.statusLinter = this.statusBar.find('.status-linter')
this.statusPreferences = this.statusBar.find('.status-preferences')
this.statusPanel = this.editor.addPanel(this.statusBar[0], {
position: 'bottom'
@@ -239,6 +323,7 @@ export default class Editor {
this.setKeymap()
this.setTheme()
this.setSpellcheck()
+ this.setLinter()
this.setPreferences()
}
@@ -497,6 +582,42 @@ export default class Editor {
}
}
+ toggleLinter (enable) {
+ const gutters = this.editor.getOption('gutters')
+ const lintGutter = 'CodeMirror-lint-markers'
+
+ if (enable) {
+ if (!gutters.includes(lintGutter)) {
+ this.editor.setOption('gutters', [lintGutter, ...gutters])
+ }
+ Cookies.set('linter', true, {
+ expires: 365
+ })
+ } else {
+ this.editor.setOption('gutters', gutters.filter(g => g !== lintGutter))
+ Cookies.remove('linter')
+ }
+ this.editor.setOption('lint', enable)
+ }
+
+ setLinter () {
+ const linterToggle = this.statusLinter.find('.ui-linter-toggle')
+
+ const updateLinterStatus = (enable) => {
+ linterToggle.toggleClass('active', enable)
+ }
+
+ linterToggle.click(() => {
+ const lintEnable = this.editor.getOption('lint')
+ this.toggleLinter.bind(this)(!lintEnable)
+ updateLinterStatus(!lintEnable)
+ })
+
+ const enable = !!Cookies.get('linter')
+ this.toggleLinter.bind(this)(enable)
+ updateLinterStatus(enable)
+ }
+
resetEditorKeymapToBrowserKeymap () {
var keymap = this.editor.getOption('keyMap')
if (!this.jumpToAddressBarKeymapValue) {
@@ -512,6 +633,7 @@ export default class Editor {
this.jumpToAddressBarKeymapValue = null
}
}
+
setOverrideBrowserKeymap () {
var overrideBrowserKeymap = $(
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
@@ -582,6 +704,8 @@ export default class Editor {
placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)"
})
+ this.tableEditor = initTableEditor(this.editor)
+
return this.editor
}
diff --git a/public/js/lib/editor/markdown-lint/index.js b/public/js/lib/editor/markdown-lint/index.js
new file mode 100644
index 00000000..1b92ece2
--- /dev/null
+++ b/public/js/lib/editor/markdown-lint/index.js
@@ -0,0 +1,48 @@
+/* global CodeMirror */
+
+// load CM lint plugin explicitly
+import '@hackmd/codemirror/addon/lint/lint'
+import './lint.css'
+
+window.markdownit = require('markdown-it')
+// eslint-disable-next-line
+require('script-loader!markdownlint');
+
+(function (mod) {
+ mod(CodeMirror)
+})(function (CodeMirror) {
+ function validator (text) {
+ return lint(text).map(error => {
+ const {
+ ruleNames,
+ ruleDescription,
+ lineNumber: ln,
+ errorRange
+ } = error
+ const lineNumber = ln - 1
+
+ let start = 0; let end = -1
+ if (errorRange) {
+ [start, end] = errorRange.map(r => r - 1)
+ }
+
+ return {
+ messageHTML: `${ruleNames.join('/')}: ${ruleDescription}`,
+ severity: 'error',
+ from: CodeMirror.Pos(lineNumber, start),
+ to: CodeMirror.Pos(lineNumber, end)
+ }
+ })
+ }
+
+ CodeMirror.registerHelper('lint', 'markdown', validator)
+})
+
+function lint (content) {
+ const { content: errors } = window.markdownlint.sync({
+ strings: {
+ content
+ }
+ })
+ return errors
+}
diff --git a/public/js/lib/editor/markdown-lint/lint.css b/public/js/lib/editor/markdown-lint/lint.css
new file mode 100644
index 00000000..0244eae2
--- /dev/null
+++ b/public/js/lib/editor/markdown-lint/lint.css
@@ -0,0 +1,73 @@
+/* The lint marker gutter */
+.CodeMirror-lint-markers {
+ width: 16px;
+}
+
+.CodeMirror-lint-tooltip {
+ background-color: #333333;
+ border: 1px solid #eeeeee;
+ border-radius: 4px;
+ color: white;
+ font-family: "Source Code Pro", Consolas, monaco, monospace;
+ font-size: 10pt;
+ overflow: hidden;
+ padding: 2px 5px;
+ position: fixed;
+ white-space: pre;
+ white-space: pre-wrap;
+ z-index: 100;
+ max-width: 600px;
+ opacity: 0;
+ transition: opacity .4s;
+ -moz-transition: opacity .4s;
+ -webkit-transition: opacity .4s;
+ -o-transition: opacity .4s;
+ -ms-transition: opacity .4s;
+}
+
+.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
+ background-position: left bottom;
+ background-repeat: repeat-x;
+}
+
+.CodeMirror-lint-mark-error {
+ background-image: url(/images/lint/mark-error.png);
+}
+
+.CodeMirror-lint-mark-warning {
+ background-image: url(/images/lint/mark-warning.png);
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
+ background-position: center center;
+ background-repeat: no-repeat;
+ cursor: pointer;
+ display: inline-block;
+ height: 16px;
+ width: 16px;
+ vertical-align: middle;
+ position: relative;
+ margin-left: 5px;
+}
+
+.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
+ padding-left: 20px;
+ background-position: top left;
+ background-repeat: no-repeat;
+ background-position-y: 2px;
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
+ background-image: url(/images/lint/message-error.png);
+}
+
+.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
+ background-image: url(/images/lint/message-warning.png);
+}
+
+.CodeMirror-lint-marker-multiple {
+ background-image: url(/images/lint/mark-multiple.png);
+ background-repeat: no-repeat;
+ background-position: right bottom;
+ width: 100%; height: 100%;
+}
diff --git a/public/js/lib/editor/statusbar.html b/public/js/lib/editor/statusbar.html
index 24cbf6c2..c4c796bc 100644
--- a/public/js/lib/editor/statusbar.html
+++ b/public/js/lib/editor/statusbar.html
@@ -37,5 +37,8 @@
+