392 lines
13 KiB
JavaScript
392 lines
13 KiB
JavaScript
import listEditor from 'wiki-list-editor';
|
|
import MarkdownIt from 'markdown-it';
|
|
import MarkdownItWikilinks from './wikilinks';
|
|
import MarkdownItMark from 'markdown-it-mark';
|
|
import axios from 'axios';
|
|
import qs from 'querystring'
|
|
import $ from 'jquery';
|
|
import search from './search';
|
|
import createPageSearch from './fuse';
|
|
import util from './util';
|
|
import Mustache from 'mustache';
|
|
import 'jquery-contextmenu';
|
|
import getCaretCoordinates from './caret-position'
|
|
import moment from 'moment'
|
|
import PrismJS from 'prismjs'
|
|
import 'prismjs/components/prism-php'
|
|
import 'prismjs/components/prism-markup-templating'
|
|
import './styles.scss';
|
|
import '../node_modules/jquery-contextmenu/dist/jquery.contextMenu.css';
|
|
|
|
moment.locale('nl')
|
|
|
|
function isMultiline(input) {
|
|
return input.value.startsWith("```", 0)
|
|
}
|
|
|
|
function addSaver(editor, saveUrl, page, beforeSave) {
|
|
return {
|
|
save() {
|
|
return editor.save().then(outputData => {
|
|
beforeSave()
|
|
let data = {
|
|
'json': 1,
|
|
'p': page,
|
|
'summary': "",
|
|
'content': JSON.stringify(outputData),
|
|
};
|
|
return axios.post(saveUrl, qs.encode(data))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
function Indicator(element, timeout) {
|
|
let timeoutId;
|
|
|
|
return {
|
|
done() {
|
|
timeoutId = setTimeout(() => {
|
|
element.classList.add('hidden')
|
|
}, timeout * 1000);
|
|
},
|
|
|
|
setText(text) {
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = null;
|
|
}
|
|
element.innerText = text;
|
|
element.classList.remove('hidden')
|
|
},
|
|
}
|
|
}
|
|
|
|
function addIndicator(editor, indicator) {
|
|
return {
|
|
save() {
|
|
editor.save().then(() => {
|
|
indicator.setText('saved!');
|
|
indicator.done();
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
let holder = document.getElementById('editor');
|
|
|
|
function showSearchResults(searchTool, query, input, value, resultType) {
|
|
showSearchResultsExtended('#link-complete', 'link-template', searchTool, query, input, value, resultType, {belowCursor: true})
|
|
}
|
|
|
|
function showSearchResultsExtended(element, template, searchTool, query, input, value, resultType, options) {
|
|
const $lc = $(element)
|
|
let results = searchTool(query)
|
|
|
|
if (query.length === 0 || !results.length) {
|
|
$lc.fadeOut()
|
|
return
|
|
}
|
|
|
|
let opt = options || {};
|
|
|
|
$lc.data('result-type', resultType)
|
|
|
|
if (opt.belowCursor) {
|
|
let pos = getCaretCoordinates(input, value.selectionEnd, {})
|
|
let off = $(input).offset()
|
|
pos.top += off.top + pos.height
|
|
pos.left += off.left
|
|
$lc.offset(pos)
|
|
}
|
|
|
|
var templateText = document.getElementById(template).innerHTML;
|
|
var rendered = Mustache.render(templateText, {page: value, results: results}, {}, ['[[', ']]']);
|
|
$lc.html(rendered).fadeIn()
|
|
}
|
|
|
|
$(document).on('keydown', '.keyboard-list', function (event) {
|
|
console.log('keydown .keyboard-list')
|
|
if (!$(':visible', this).length) {
|
|
return true
|
|
}
|
|
|
|
const $popup = $(this)
|
|
if (event.key === 'Escape') {
|
|
$popup.fadeOut()
|
|
return false
|
|
}
|
|
if (event.key === 'Enter') {
|
|
const element = $popup.find('li.selected')
|
|
const linkName = element.text()
|
|
$popup.trigger('popup:selected', [linkName, $popup.data('result-type'), element])
|
|
$popup.fadeOut()
|
|
return false
|
|
}
|
|
if (event.key === 'ArrowUp') {
|
|
const selected = $popup.find('li.selected')
|
|
const prev = selected.prev('li')
|
|
if (prev.length) {
|
|
prev.addClass('selected')
|
|
selected.removeClass('selected')
|
|
prev[0].scrollIntoView({block: 'center', inline: 'nearest'})
|
|
} else {
|
|
// move back from dropdown to input
|
|
$popup.trigger('popup:leave')
|
|
selected.removeClass('selected')
|
|
}
|
|
return false
|
|
}
|
|
if (event.key === 'ArrowDown') {
|
|
const selected = $popup.find('li.selected')
|
|
const next = selected.next('li');
|
|
if (next.length) {
|
|
next.addClass('selected')
|
|
selected.removeClass('selected')
|
|
next[0].scrollIntoView({block: 'center', inline: 'nearest'})
|
|
}
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
$(document).on('keydown', '#search-input', function (event) {
|
|
console.log('keydown #search-input')
|
|
let $ac = $('#autocomplete');
|
|
let isVisible = $(':visible', $ac)
|
|
if (event.key === 'ArrowDown' && isVisible) {
|
|
$ac.focus()
|
|
$ac.find('li:first-child').addClass('selected')
|
|
return false
|
|
}
|
|
if (event.key === 'Escape') {
|
|
$(searchInput).val('');
|
|
$ac.fadeOut();
|
|
return false;
|
|
}
|
|
return true
|
|
})
|
|
|
|
$(document).on('popup:selected', '#autocomplete', function (event, linkName, resultType, element) {
|
|
if (resultType === 'search-result') {
|
|
element.find('a')[0].click()
|
|
return false;
|
|
}
|
|
})
|
|
|
|
if (holder) {
|
|
console.log(holder.dataset)
|
|
const MD = new MarkdownIt()
|
|
MD.use(MarkdownItWikilinks({
|
|
baseURL: holder.dataset.baseUrl,
|
|
uriSuffix: '',
|
|
relativeBaseURL: '/edit/',
|
|
htmlAttributes: {
|
|
class: 'wiki-link'
|
|
}
|
|
})).use(MarkdownItMark)
|
|
|
|
const options = {
|
|
transform(text, callback) {
|
|
let converted = (text.startsWith("```", 0)) ? MD.render(text) : MD.renderInline(text)
|
|
return callback(converted)
|
|
}
|
|
}
|
|
|
|
let editor = listEditor(holder, JSON.parse(holder.dataset.input), options);
|
|
|
|
editor.on('change', function () {
|
|
let element = document.getElementById('editor');
|
|
let indicator = Indicator(document.getElementById('save-indicator'), 2);
|
|
let saveUrl = element.dataset.saveurl;
|
|
let page = element.dataset.page;
|
|
|
|
indicator.setText('has changes...');
|
|
addIndicator(
|
|
addSaver(editor, saveUrl, page, () => indicator.setText('saving...')),
|
|
indicator
|
|
).save()
|
|
})
|
|
|
|
createPageSearch().then(function ({titleSearch, commandSearch, commands}) {
|
|
editor.on('start-editing', function (input) {
|
|
const $lc = $('#link-complete');
|
|
|
|
$(input).parents('.list-item').addClass('active');
|
|
|
|
$lc.on('popup:selected', function (event, linkName, resultType, element) {
|
|
let value = input.value
|
|
let end = input.selectionEnd
|
|
if (resultType === 'link') {
|
|
let start = value.lastIndexOf("[[", end)
|
|
end += 2
|
|
let startAndLink = value.substring(0, start) + "[[" + linkName + "]]"
|
|
input.value = startAndLink + value.substring(end)
|
|
input.selectionStart = startAndLink.length
|
|
input.selectionEnd = startAndLink.length
|
|
input.focus()
|
|
} else if (resultType === 'command') {
|
|
let start = value.lastIndexOf("/", end)
|
|
let commandResult = ""
|
|
let replace = ""
|
|
let prefix = false
|
|
let adjustment = 0
|
|
|
|
let now = moment()
|
|
|
|
if (linkName === "Current Time") {
|
|
commandResult = now.format('HH:mm')
|
|
} else if (linkName === "Today") {
|
|
commandResult = "[[" + now.format('D MMMM YYYY') + "]]"
|
|
} else if (linkName === "Tomorrow") {
|
|
commandResult = "[[" + now.add(1, 'day').format('D MMMM YYYY') + "]]"
|
|
} else if (linkName === "Yesterday") {
|
|
commandResult = "[[" + now.add(-1, 'day').format('D MMMM YYYY') + "]]"
|
|
} else if (linkName === "TODO") {
|
|
commandResult = "#[[TODO]] "
|
|
replace = "#[[DONE]] "
|
|
prefix = true
|
|
} else if (linkName === "DONE") {
|
|
commandResult = "#[[DONE]] "
|
|
replace = "#[[TODO]] "
|
|
prefix = true
|
|
} else if (linkName === "Page Reference") {
|
|
commandResult = "[[]]"
|
|
adjustment = -2
|
|
} else if (linkName === "Code Block") {
|
|
commandResult = "```\n\n```"
|
|
adjustment = -5
|
|
}
|
|
|
|
let startAndLink = prefix
|
|
? commandResult + value.substring(0, start).replace(replace, "")
|
|
: value.substring(0, start) + commandResult
|
|
|
|
input.value = startAndLink + value.substring(end)
|
|
|
|
input.selectionStart = startAndLink.length + adjustment
|
|
input.selectionEnd = startAndLink.length + adjustment
|
|
|
|
input.focus()
|
|
}
|
|
return true
|
|
})
|
|
|
|
$lc.on('popup:leave', function (event) {
|
|
input.focus()
|
|
})
|
|
|
|
$(input).on('keydown', function (event) {
|
|
const isVisible = $('#link-complete:visible').length > 0;
|
|
|
|
if (event.key === 'Escape' && isVisible) {
|
|
$lc.fadeOut()
|
|
return false
|
|
}
|
|
|
|
if (event.key === 'ArrowDown' && isVisible) {
|
|
$lc.focus()
|
|
$lc.find('li:first-child').addClass('selected')
|
|
return false
|
|
}
|
|
|
|
let mirror = {
|
|
'[': ']',
|
|
'=': '=',
|
|
}
|
|
|
|
if (!isMultiline(input) && mirror.hasOwnProperty(event.key)) {
|
|
let input = this
|
|
let val = input.value
|
|
let prefix = val.substring(0, input.selectionStart)
|
|
let selection = val.substring(input.selectionStart, input.selectionEnd)
|
|
let suffix = val.substring(input.selectionEnd)
|
|
input.value = prefix + event.key + selection + mirror[event.key] + suffix
|
|
input.selectionStart = prefix.length + event.key.length
|
|
input.selectionEnd = input.selectionStart + selection.length
|
|
$(input).trigger('input')
|
|
return false;
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
$(input).on('keyup', function () {
|
|
let value = input.value
|
|
let end = input.selectionEnd
|
|
|
|
let [start, insideLink] = util.cursorInsideLink(value, end)
|
|
console.log(value, end, start, insideLink)
|
|
let insideSearch = false
|
|
|
|
if (!insideLink) {
|
|
start = value.lastIndexOf("/", end)
|
|
insideSearch = start >= 0
|
|
}
|
|
|
|
if (insideSearch) {
|
|
let query = value.substring(start + 1, end);
|
|
showSearchResults(query => commandSearch.search(query), query, input, value, 'command');
|
|
return true
|
|
} else if (insideLink) {
|
|
let query = value.substring(start + 2, end);
|
|
showSearchResults(query => titleSearch.search(query), query, input, value, 'link');
|
|
return true
|
|
} else {
|
|
$('#link-complete').fadeOut();
|
|
}
|
|
})
|
|
})
|
|
|
|
editor.on('stop-editing', function (input) {
|
|
$(input).parents('.list-item').removeClass('active');
|
|
$('#link-complete').off()
|
|
PrismJS.highlightAll()
|
|
})
|
|
})
|
|
$.contextMenu({
|
|
selector: '.marker',
|
|
items: {
|
|
copy: {
|
|
name: 'Copy',
|
|
callback: function (key, opt) {
|
|
editor.copy(this).then(result => {
|
|
console.log(result)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
let timeout = null;
|
|
let searchInput = document.getElementById('search-input');
|
|
search(searchInput).then(searcher => {
|
|
|
|
$(document).on('keyup', '#search-input', function (event) {
|
|
clearTimeout(timeout);
|
|
|
|
timeout = setTimeout(function () {
|
|
let query = $(searchInput).val()
|
|
if (query === '') {
|
|
let autocomplete = document.getElementById('autocomplete');
|
|
$(autocomplete).hide()
|
|
return false
|
|
}
|
|
|
|
showSearchResultsExtended('#autocomplete', 'result-template', query => searcher.search(query), query, searchInput, query, 'search-result')
|
|
|
|
// $('#autocomplete').show()
|
|
// let result = searcher.search(query)
|
|
// const newpage = query.replace(/\s+/g, '_')
|
|
// var template = document.getElementById('result-template').innerHTML;
|
|
// var rendered = Mustache.render(template, {page: newpage, results: result}, {}, ['[[', ']]']);
|
|
// let autocomplete = document.getElementById('autocomplete');
|
|
// autocomplete.innerHTML = rendered;
|
|
}, 200)
|
|
|
|
return true
|
|
})
|
|
})
|