Merge branch 'master' of https://github.com/jackycute/HackMD
This commit is contained in:
commit
b07eeed0c5
7 changed files with 687 additions and 564 deletions
|
@ -3,7 +3,7 @@ root = true
|
||||||
# Tab indentation
|
# Tab indentation
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
HackMD
|
HackMD
|
||||||
===
|
===
|
||||||
|
|
||||||
[![Standard - JavaScript Style Guide](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
|
[![Standard - JavaScript Style Guide][standardjs-image]][standardjs-url]
|
||||||
|
|
||||||
[![Join the chat at https://gitter.im/hackmdio/hackmd][gitter-image]][gitter-url]
|
[![Join the chat at https://gitter.im/hackmdio/hackmd][gitter-image]][gitter-url]
|
||||||
[![build status][travis-image]][travis-url]
|
[![build status][travis-image]][travis-url]
|
||||||
|
@ -227,7 +227,10 @@ Additionally, now can show other clients' selections.
|
||||||
See more at [http://operational-transformation.github.io/](http://operational-transformation.github.io/)
|
See more at [http://operational-transformation.github.io/](http://operational-transformation.github.io/)
|
||||||
|
|
||||||
**License under MIT.**
|
**License under MIT.**
|
||||||
|
|
||||||
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
||||||
[gitter-url]: https://gitter.im/hackmdio/hackmd?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
[gitter-url]: https://gitter.im/hackmdio/hackmd?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||||
[travis-image]: https://travis-ci.org/hackmdio/hackmd.svg?branch=master
|
[travis-image]: https://travis-ci.org/hackmdio/hackmd.svg?branch=master
|
||||||
[travis-url]: https://travis-ci.org/hackmdio/hackmd
|
[travis-url]: https://travis-ci.org/hackmdio/hackmd
|
||||||
|
[standardjs-image]: https://cdn.rawgit.com/feross/standard/master/badge.svg
|
||||||
|
[standardjs-url]: https://github.com/feross/standard
|
||||||
|
|
21
package.json
21
package.json
|
@ -169,6 +169,27 @@
|
||||||
"webpack-parallel-uglify-plugin": "^0.2.0"
|
"webpack-parallel-uglify-plugin": "^0.2.0"
|
||||||
},
|
},
|
||||||
"standard": {
|
"standard": {
|
||||||
|
"globals": [
|
||||||
|
"$",
|
||||||
|
"CodeMirror",
|
||||||
|
"Cookies",
|
||||||
|
"moment", "editor",
|
||||||
|
"ui",
|
||||||
|
"Spinner",
|
||||||
|
"modeType",
|
||||||
|
"Idle",
|
||||||
|
"serverurl",
|
||||||
|
"key",
|
||||||
|
"gapi",
|
||||||
|
"Dropbox",
|
||||||
|
"FilePicker",
|
||||||
|
"ot",
|
||||||
|
"MediaUploader",
|
||||||
|
"hex2rgb",
|
||||||
|
"num_loaded",
|
||||||
|
"Visibility",
|
||||||
|
"inlineAttachment"
|
||||||
|
],
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"lib/ot",
|
"lib/ot",
|
||||||
"public/vendor"
|
"public/vendor"
|
||||||
|
|
|
@ -78,141 +78,13 @@ import {
|
||||||
var renderer = require('./render')
|
var renderer = require('./render')
|
||||||
var preventXSS = renderer.preventXSS
|
var preventXSS = renderer.preventXSS
|
||||||
|
|
||||||
|
import Editor from './lib/editor'
|
||||||
|
|
||||||
|
import getUIElements from './lib/editor/ui-elements'
|
||||||
|
|
||||||
var defaultTextHeight = 20
|
var defaultTextHeight = 20
|
||||||
var viewportMargin = 20
|
var viewportMargin = 20
|
||||||
var mac = CodeMirror.keyMap['default'] === CodeMirror.keyMap.macDefault
|
|
||||||
var defaultEditorMode = 'gfm'
|
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
|
|
||||||
} 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 = 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')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var idleTime = 300000 // 5 mins
|
var idleTime = 300000 // 5 mins
|
||||||
var updateViewDebounce = 100
|
var updateViewDebounce = 100
|
||||||
|
@ -435,343 +307,23 @@ window.fileTypes = {
|
||||||
|
|
||||||
// editor settings
|
// editor settings
|
||||||
var textit = document.getElementById('textit')
|
var textit = document.getElementById('textit')
|
||||||
if (!textit) throw new Error('There was no textit area!')
|
if (!textit) {
|
||||||
window.editor = CodeMirror.fromTextArea(textit, {
|
throw new Error('There was no textit area!')
|
||||||
mode: defaultEditorMode,
|
}
|
||||||
backdrop: defaultEditorMode,
|
|
||||||
keyMap: 'sublime',
|
const editorInstance = new Editor()
|
||||||
viewportMargin: viewportMargin,
|
var editor = editorInstance.init(textit)
|
||||||
styleActiveLine: true,
|
|
||||||
lineNumbers: true,
|
// TODO: global referncing in jquery-textcomplete patch
|
||||||
lineWrapping: true,
|
window.editor = editor
|
||||||
showCursorWhenSelecting: true,
|
|
||||||
highlightSelectionMatches: true,
|
var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor)
|
||||||
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'))
|
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'
|
|
||||||
}
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
var selection = null
|
||||||
|
|
||||||
function updateStatusBar () {
|
function updateStatusBar () {
|
||||||
if (!statusBar) return
|
if (!editorInstance.statusBar) return
|
||||||
var cursor = editor.getCursor()
|
var cursor = editor.getCursor()
|
||||||
var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1)
|
var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1)
|
||||||
if (selection) {
|
if (selection) {
|
||||||
|
@ -781,112 +333,41 @@ function updateStatusBar () {
|
||||||
var end = head.line >= anchor.line ? head : anchor
|
var end = head.line >= anchor.line ? head : anchor
|
||||||
var selectionText = ' — Selected '
|
var selectionText = ' — Selected '
|
||||||
var selectionCharCount = Math.abs(head.ch - anchor.ch)
|
var selectionCharCount = Math.abs(head.ch - anchor.ch)
|
||||||
// borrow from brackets EditorStatusBar.js
|
// borrow from brackets EditorStatusBar.js
|
||||||
if (start.line !== end.line) {
|
if (start.line !== end.line) {
|
||||||
var lines = end.line - start.line + 1
|
var lines = end.line - start.line + 1
|
||||||
if (end.ch === 0) {
|
if (end.ch === 0) {
|
||||||
lines--
|
lines--
|
||||||
}
|
}
|
||||||
selectionText += lines + ' lines'
|
selectionText += lines + ' lines'
|
||||||
} else if (selectionCharCount > 0) { selectionText += selectionCharCount + ' columns' }
|
} else if (selectionCharCount > 0) {
|
||||||
if (start.line !== end.line || selectionCharCount > 0) { cursorText += selectionText }
|
selectionText += selectionCharCount + ' columns'
|
||||||
|
}
|
||||||
|
if (start.line !== end.line || selectionCharCount > 0) {
|
||||||
|
cursorText += selectionText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
statusCursor.text(cursorText)
|
editorInstance.statusCursor.text(cursorText)
|
||||||
var fileText = ' — ' + editor.lineCount() + ' Lines'
|
var fileText = ' — ' + editor.lineCount() + ' Lines'
|
||||||
statusFile.text(fileText)
|
editorInstance.statusFile.text(fileText)
|
||||||
var docLength = editor.getValue().length
|
var docLength = editor.getValue().length
|
||||||
statusLength.text('Length ' + docLength)
|
editorInstance.statusLength.text('Length ' + docLength)
|
||||||
if (docLength > (docmaxlength * 0.95)) {
|
if (docLength > (docmaxlength * 0.95)) {
|
||||||
statusLength.css('color', 'red')
|
editorInstance.statusLength.css('color', 'red')
|
||||||
statusLength.attr('title', 'Your almost reach note max length limit.')
|
editorInstance.statusLength.attr('title', 'Your almost reach note max length limit.')
|
||||||
} else if (docLength > (docmaxlength * 0.8)) {
|
} else if (docLength > (docmaxlength * 0.8)) {
|
||||||
statusLength.css('color', 'orange')
|
editorInstance.statusLength.css('color', 'orange')
|
||||||
statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.')
|
editorInstance.statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.')
|
||||||
} else {
|
} else {
|
||||||
statusLength.css('color', 'white')
|
editorInstance.statusLength.css('color', 'white')
|
||||||
statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.')
|
editorInstance.statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ui vars
|
// initalize ui reference
|
||||||
window.ui = {
|
// TODO: fix ui exporting
|
||||||
spinner: $('.ui-spinner'),
|
const ui = getUIElements()
|
||||||
content: $('.ui-content'),
|
window.ui = ui
|
||||||
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')
|
|
||||||
},
|
|
||||||
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
|
// page actions
|
||||||
var opts = {
|
var opts = {
|
||||||
|
@ -1135,7 +616,7 @@ var lastEditorWidth = 0
|
||||||
var previousFocusOnEditor = null
|
var previousFocusOnEditor = null
|
||||||
|
|
||||||
function checkEditorStyle () {
|
function checkEditorStyle () {
|
||||||
var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height()
|
var desireHeight = editorInstance.statusBar ? (ui.area.edit.height() - editorInstance.statusBar.outerHeight()) : ui.area.edit.height()
|
||||||
// set editor height and min height based on scrollbar style and mode
|
// set editor height and min height based on scrollbar style and mode
|
||||||
var scrollbarStyle = editor.getOption('scrollbarStyle')
|
var scrollbarStyle = editor.getOption('scrollbarStyle')
|
||||||
if (scrollbarStyle === 'overlay' || window.currentMode === modeType.both) {
|
if (scrollbarStyle === 'overlay' || window.currentMode === modeType.both) {
|
||||||
|
@ -1369,12 +850,12 @@ function changeMode (type) {
|
||||||
}
|
}
|
||||||
if (window.currentMode === modeType.edit || window.currentMode === modeType.both) {
|
if (window.currentMode === modeType.edit || window.currentMode === modeType.both) {
|
||||||
ui.toolbar.uploadImage.fadeIn()
|
ui.toolbar.uploadImage.fadeIn()
|
||||||
// add and update status bar
|
// add and update status bar
|
||||||
if (!statusBar) {
|
if (!editorInstance.statusBar) {
|
||||||
addStatusBar()
|
editorInstance.addStatusBar()
|
||||||
updateStatusBar()
|
updateStatusBar()
|
||||||
}
|
}
|
||||||
// work around foldGutter might not init properly
|
// work around foldGutter might not init properly
|
||||||
editor.setOption('foldGutter', false)
|
editor.setOption('foldGutter', false)
|
||||||
editor.setOption('foldGutter', true)
|
editor.setOption('foldGutter', true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1388,7 +869,11 @@ function changeMode (type) {
|
||||||
}
|
}
|
||||||
// check resizable editor style
|
// check resizable editor style
|
||||||
if (window.currentMode === modeType.both) {
|
if (window.currentMode === modeType.both) {
|
||||||
if (lastEditorWidth > 0) { ui.area.edit.css('width', lastEditorWidth + 'px') } else { ui.area.edit.css('width', '') }
|
if (lastEditorWidth > 0) {
|
||||||
|
ui.area.edit.css('width', lastEditorWidth + 'px')
|
||||||
|
} else {
|
||||||
|
ui.area.edit.css('width', '')
|
||||||
|
}
|
||||||
ui.area.resize.handle.show()
|
ui.area.resize.handle.show()
|
||||||
} else {
|
} else {
|
||||||
ui.area.edit.css('width', '')
|
ui.area.edit.css('width', '')
|
||||||
|
@ -3924,6 +3409,6 @@ $(editor.getInputField())
|
||||||
},
|
},
|
||||||
'textComplete:hide': function (e) {
|
'textComplete:hide': function (e) {
|
||||||
$(this).data('autocompleting', false)
|
$(this).data('autocompleting', false)
|
||||||
editor.setOption('extraKeys', defaultExtraKeys)
|
editor.setOption('extraKeys', editorInstance.defaultExtraKeys)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
480
public/js/lib/editor/index.js
Normal file
480
public/js/lib/editor/index.js
Normal file
|
@ -0,0 +1,480 @@
|
||||||
|
import * as utils from './utils'
|
||||||
|
|
||||||
|
/* config section */
|
||||||
|
const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault
|
||||||
|
const defaultEditorMode = 'gfm'
|
||||||
|
const viewportMargin = 20
|
||||||
|
|
||||||
|
const jumpToAddressBarKeymapName = isMac ? 'Cmd-L' : 'Ctrl-L'
|
||||||
|
|
||||||
|
export default class Editor {
|
||||||
|
constructor () {
|
||||||
|
this.editor = null
|
||||||
|
this.jumpToAddressBarKeymapValue = null
|
||||||
|
this.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'
|
||||||
|
|
||||||
|
// contruct x length spaces
|
||||||
|
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)
|
||||||
|
|
||||||
|
// this regex match the following patterns
|
||||||
|
// 1. blockquote starts with "> " or ">>"
|
||||||
|
// 2. unorder list starts with *+-
|
||||||
|
// 3. order list starts with "1." or "1)"
|
||||||
|
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 (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') {
|
||||||
|
document.execCommand('copy')
|
||||||
|
} else {
|
||||||
|
return CodeMirror.Pass
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Ctrl-*': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '*')
|
||||||
|
},
|
||||||
|
'Shift-Ctrl-8': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '*')
|
||||||
|
},
|
||||||
|
'Ctrl-_': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '_')
|
||||||
|
},
|
||||||
|
'Shift-Ctrl--': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '_')
|
||||||
|
},
|
||||||
|
'Ctrl-~': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '~')
|
||||||
|
},
|
||||||
|
'Shift-Ctrl-`': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '~')
|
||||||
|
},
|
||||||
|
'Ctrl-^': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '^')
|
||||||
|
},
|
||||||
|
'Shift-Ctrl-6': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '^')
|
||||||
|
},
|
||||||
|
'Ctrl-+': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '+')
|
||||||
|
},
|
||||||
|
'Shift-Ctrl-=': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '+')
|
||||||
|
},
|
||||||
|
'Ctrl-=': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, '=')
|
||||||
|
},
|
||||||
|
'Shift-Ctrl-Backspace': cm => {
|
||||||
|
utils.wrapTextWith(this.editor, cm, 'Backspace')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusBarTemplate (callback) {
|
||||||
|
$.get(window.serverurl + '/views/statusbar.html', template => {
|
||||||
|
this.statusBarTemplate = template
|
||||||
|
if (callback) callback()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addStatusBar () {
|
||||||
|
if (!this.statusBarTemplate) {
|
||||||
|
this.getStatusBarTemplate(this.addStatusBar)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.statusBar = $(this.statusBarTemplate)
|
||||||
|
this.statusCursor = this.statusBar.find('.status-cursor')
|
||||||
|
this.statusFile = this.statusBar.find('.status-file')
|
||||||
|
this.statusIndicators = this.statusBar.find('.status-indicators')
|
||||||
|
this.statusIndent = this.statusBar.find('.status-indent')
|
||||||
|
this.statusKeymap = this.statusBar.find('.status-keymap')
|
||||||
|
this.statusLength = this.statusBar.find('.status-length')
|
||||||
|
this.statusTheme = this.statusBar.find('.status-theme')
|
||||||
|
this.statusSpellcheck = this.statusBar.find('.status-spellcheck')
|
||||||
|
this.statusPreferences = this.statusBar.find('.status-preferences')
|
||||||
|
this.statusPanel = this.editor.addPanel(this.statusBar[0], {
|
||||||
|
position: 'bottom'
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setIndent()
|
||||||
|
this.setKeymap()
|
||||||
|
this.setTheme()
|
||||||
|
this.setSpellcheck()
|
||||||
|
this.setPreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
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') {
|
||||||
|
this.editor.setOption('indentWithTabs', true)
|
||||||
|
if (cookieTabSize) {
|
||||||
|
this.editor.setOption('indentUnit', cookieTabSize)
|
||||||
|
}
|
||||||
|
} else if (cookieIndentType === 'space') {
|
||||||
|
this.editor.setOption('indentWithTabs', false)
|
||||||
|
if (cookieSpaceUnits) {
|
||||||
|
this.editor.setOption('indentUnit', cookieSpaceUnits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cookieTabSize) {
|
||||||
|
this.editor.setOption('tabSize', cookieTabSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = this.statusIndicators.find('.indent-type')
|
||||||
|
var widthLabel = this.statusIndicators.find('.indent-width-label')
|
||||||
|
var widthInput = this.statusIndicators.find('.indent-width-input')
|
||||||
|
|
||||||
|
const setType = () => {
|
||||||
|
if (this.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()
|
||||||
|
|
||||||
|
const setUnit = () => {
|
||||||
|
var unit = this.editor.getOption('indentUnit')
|
||||||
|
if (this.editor.getOption('indentWithTabs')) {
|
||||||
|
Cookies.set('tab_size', unit, {
|
||||||
|
expires: 365
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Cookies.set('space_units', unit, {
|
||||||
|
expires: 365
|
||||||
|
})
|
||||||
|
}
|
||||||
|
widthLabel.text(unit)
|
||||||
|
}
|
||||||
|
setUnit()
|
||||||
|
|
||||||
|
type.click(() => {
|
||||||
|
if (this.editor.getOption('indentWithTabs')) {
|
||||||
|
this.editor.setOption('indentWithTabs', false)
|
||||||
|
cookieSpaceUnits = parseInt(Cookies.get('space_units'))
|
||||||
|
if (cookieSpaceUnits) {
|
||||||
|
this.editor.setOption('indentUnit', cookieSpaceUnits)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.editor.setOption('indentWithTabs', true)
|
||||||
|
cookieTabSize = parseInt(Cookies.get('tab_size'))
|
||||||
|
if (cookieTabSize) {
|
||||||
|
this.editor.setOption('indentUnit', cookieTabSize)
|
||||||
|
this.editor.setOption('tabSize', cookieTabSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setType()
|
||||||
|
setUnit()
|
||||||
|
})
|
||||||
|
widthLabel.click(() => {
|
||||||
|
if (widthLabel.is(':visible')) {
|
||||||
|
widthLabel.addClass('hidden')
|
||||||
|
widthInput.removeClass('hidden')
|
||||||
|
widthInput.val(this.editor.getOption('indentUnit'))
|
||||||
|
widthInput.select()
|
||||||
|
} else {
|
||||||
|
widthLabel.removeClass('hidden')
|
||||||
|
widthInput.addClass('hidden')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
widthInput.on('change', () => {
|
||||||
|
var val = parseInt(widthInput.val())
|
||||||
|
if (!val) val = this.editor.getOption('indentUnit')
|
||||||
|
if (val < 1) val = 1
|
||||||
|
else if (val > 10) val = 10
|
||||||
|
|
||||||
|
if (this.editor.getOption('indentWithTabs')) {
|
||||||
|
this.editor.setOption('tabSize', val)
|
||||||
|
}
|
||||||
|
this.editor.setOption('indentUnit', val)
|
||||||
|
setUnit()
|
||||||
|
})
|
||||||
|
widthInput.on('blur', function () {
|
||||||
|
widthLabel.removeClass('hidden')
|
||||||
|
widthInput.addClass('hidden')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeymap () {
|
||||||
|
var cookieKeymap = Cookies.get('keymap')
|
||||||
|
if (cookieKeymap) {
|
||||||
|
this.editor.setOption('keyMap', cookieKeymap)
|
||||||
|
}
|
||||||
|
|
||||||
|
var label = this.statusIndicators.find('.ui-keymap-label')
|
||||||
|
var sublime = this.statusIndicators.find('.ui-keymap-sublime')
|
||||||
|
var emacs = this.statusIndicators.find('.ui-keymap-emacs')
|
||||||
|
var vim = this.statusIndicators.find('.ui-keymap-vim')
|
||||||
|
|
||||||
|
const setKeymapLabel = () => {
|
||||||
|
var keymap = this.editor.getOption('keyMap')
|
||||||
|
Cookies.set('keymap', keymap, {
|
||||||
|
expires: 365
|
||||||
|
})
|
||||||
|
label.text(keymap)
|
||||||
|
this.restoreOverrideEditorKeymap()
|
||||||
|
this.setOverrideBrowserKeymap()
|
||||||
|
}
|
||||||
|
setKeymapLabel()
|
||||||
|
|
||||||
|
sublime.click(() => {
|
||||||
|
this.editor.setOption('keyMap', 'sublime')
|
||||||
|
setKeymapLabel()
|
||||||
|
})
|
||||||
|
emacs.click(() => {
|
||||||
|
this.editor.setOption('keyMap', 'emacs')
|
||||||
|
setKeymapLabel()
|
||||||
|
})
|
||||||
|
vim.click(() => {
|
||||||
|
this.editor.setOption('keyMap', 'vim')
|
||||||
|
setKeymapLabel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setTheme () {
|
||||||
|
var cookieTheme = Cookies.get('theme')
|
||||||
|
if (cookieTheme) {
|
||||||
|
this.editor.setOption('theme', cookieTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeToggle = this.statusTheme.find('.ui-theme-toggle')
|
||||||
|
|
||||||
|
const checkTheme = () => {
|
||||||
|
var theme = this.editor.getOption('theme')
|
||||||
|
if (theme === 'one-dark') {
|
||||||
|
themeToggle.removeClass('active')
|
||||||
|
} else {
|
||||||
|
themeToggle.addClass('active')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
themeToggle.click(() => {
|
||||||
|
var theme = this.editor.getOption('theme')
|
||||||
|
if (theme === 'one-dark') {
|
||||||
|
theme = 'default'
|
||||||
|
} else {
|
||||||
|
theme = 'one-dark'
|
||||||
|
}
|
||||||
|
this.editor.setOption('theme', theme)
|
||||||
|
Cookies.set('theme', theme, {
|
||||||
|
expires: 365
|
||||||
|
})
|
||||||
|
|
||||||
|
checkTheme()
|
||||||
|
})
|
||||||
|
|
||||||
|
checkTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 !== this.editor.getOption('mode')) {
|
||||||
|
this.editor.setOption('mode', mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var spellcheckToggle = this.statusSpellcheck.find('.ui-spellcheck-toggle')
|
||||||
|
|
||||||
|
const checkSpellcheck = () => {
|
||||||
|
var mode = this.editor.getOption('mode')
|
||||||
|
if (mode === defaultEditorMode) {
|
||||||
|
spellcheckToggle.removeClass('active')
|
||||||
|
} else {
|
||||||
|
spellcheckToggle.addClass('active')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spellcheckToggle.click(() => {
|
||||||
|
var mode = this.editor.getOption('mode')
|
||||||
|
if (mode === defaultEditorMode) {
|
||||||
|
mode = 'spell-checker'
|
||||||
|
} else {
|
||||||
|
mode = defaultEditorMode
|
||||||
|
}
|
||||||
|
if (mode && mode !== this.editor.getOption('mode')) {
|
||||||
|
this.editor.setOption('mode', mode)
|
||||||
|
}
|
||||||
|
Cookies.set('spellcheck', mode === 'spell-checker', {
|
||||||
|
expires: 365
|
||||||
|
})
|
||||||
|
|
||||||
|
checkSpellcheck()
|
||||||
|
})
|
||||||
|
|
||||||
|
checkSpellcheck()
|
||||||
|
|
||||||
|
// workaround spellcheck might not activate beacuse the ajax loading
|
||||||
|
if (window.num_loaded < 2) {
|
||||||
|
var spellcheckTimer = setInterval(
|
||||||
|
() => {
|
||||||
|
if (window.num_loaded >= 2) {
|
||||||
|
if (this.editor.getOption('mode') === 'spell-checker') {
|
||||||
|
this.editor.setOption('mode', 'spell-checker')
|
||||||
|
}
|
||||||
|
clearInterval(spellcheckTimer)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
100,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetEditorKeymapToBrowserKeymap () {
|
||||||
|
var keymap = this.editor.getOption('keyMap')
|
||||||
|
if (!this.jumpToAddressBarKeymapValue) {
|
||||||
|
this.jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
|
||||||
|
delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreOverrideEditorKeymap () {
|
||||||
|
var keymap = this.editor.getOption('keyMap')
|
||||||
|
if (this.jumpToAddressBarKeymapValue) {
|
||||||
|
CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = this.jumpToAddressBarKeymapValue
|
||||||
|
this.jumpToAddressBarKeymapValue = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
})
|
||||||
|
this.restoreOverrideEditorKeymap()
|
||||||
|
} else {
|
||||||
|
Cookies.remove('preferences-override-browser-keymap')
|
||||||
|
this.resetEditorKeymapToBrowserKeymap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
this.setOverrideBrowserKeymap()
|
||||||
|
|
||||||
|
overrideBrowserKeymap.change(() => {
|
||||||
|
this.setOverrideBrowserKeymap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
init (textit) {
|
||||||
|
this.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: this.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 :)"
|
||||||
|
})
|
||||||
|
|
||||||
|
this.getStatusBarTemplate()
|
||||||
|
|
||||||
|
return this.editor
|
||||||
|
}
|
||||||
|
|
||||||
|
getEditor () {
|
||||||
|
return this.editor
|
||||||
|
}
|
||||||
|
}
|
86
public/js/lib/editor/ui-elements.js
Normal file
86
public/js/lib/editor/ui-elements.js
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Global UI elements references
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const getUIElements = () => ({
|
||||||
|
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')
|
||||||
|
},
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default getUIElements
|
48
public/js/lib/editor/utils.js
Normal file
48
public/js/lib/editor/utils.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
const wrapSymbols = ['*', '_', '~', '^', '+', '=']
|
||||||
|
export function wrapTextWith (editor, 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()) {
|
||||||
|
const from = range.from()
|
||||||
|
const 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)
|
||||||
|
// 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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue