Merge branch 'feature/support-drawio' of https://github.com/seanly/mindoc into seanly-feature/support-drawio

This commit is contained in:
gsw945
2023-07-03 10:11:41 +08:00
13 changed files with 11241 additions and 18 deletions

View File

@@ -3950,9 +3950,12 @@
}
return "<svg class='mindmap' style='width:100%;min-height=150px;height:"+custom_height+"px;' id='mindmap-"+ map_id +"'>"+code+"</svg>";
}
if (lang === "drawio") {
var svgCode = decodeURIComponent(escape(window.atob(code)))
return "<div class=\"svg\" style=\"overflow: auto; padding: 10px;\">" + svgCode + "</div>"
}
else
{
return marked.Renderer.prototype.code.apply(this, arguments);
}
};
@@ -4029,7 +4032,6 @@
html += "<li class=\"directory-item\"><a class=\"directory-item-link directory-item-link-" + level + "\" href=\"#" + id + "\" level=\"" + level + "\">" + text + "</a></li>";
lastLevel = level;
}
console.log(html);
var tocContainer = container.find(".markdown-toc");

View File

@@ -3,6 +3,150 @@ $(function () {
js: window.katex.js,
css: window.katex.css
};
var drawio = new Object()
drawio.processMarkers = function (from, to) {
var _this = this
var found = null
var foundStart = 0
var cm = window.editor.cm;
cm.doc.getAllMarks().forEach(mk => {
if (mk.__kind) {
mk.clear()
}
})
cm.eachLine(from, to, function (ln) {
const line = ln.lineNo()
if (ln.text.startsWith('```drawio')) {
found = 'drawio'
foundStart = line
} else if (ln.text === '```' && found) {
switch (found) {
// -> DRAWIO
case 'drawio': {
if (line - foundStart !== 2) {
return
}
_this.addMarker({
kind: 'drawio',
from: { line: foundStart, ch: 3 },
to: { line: foundStart, ch: 10 },
text: 'drawio',
action: (function (start, end) {
return function (ev) {
cm.doc.setSelection({ line: start, ch: 0 }, { line: end, ch: 3 })
try {
// save state data
const raw = cm.doc.getLine(end - 1)
window.sessionStorage.setItem("drawio", raw);
_this.show()
} catch (err) {
console.log(err)
}
}
})(foundStart, line)
})
if (ln.height > 0) {
cm.foldCode(foundStart)
}
break;
}
}
found = null
}
})
}
drawio.addMarker = function ({ kind, from, to, text, action }) {
const markerElm = document.createElement('span')
markerElm.appendChild(document.createTextNode(text))
markerElm.className = 'CodeMirror-buttonmarker'
markerElm.addEventListener('click', action)
var cm = window.editor.cm;
cm.markText(from, to, { replacedWith: markerElm, __kind: kind })
}
drawio.show = function () {
const drawUrl = 'https://embed.diagrams.net/?embed=1&libraries=1&proto=json&spin=1&saveAndExit=1&noSaveBtn=1&noExitBtn=0';
this.div = document.createElement('div');
this.div.id = 'diagram';
this.gXml = '';
this.div.innerHTML = '';
this.iframe = document.createElement('iframe');
this.iframe.setAttribute('frameborder', '0');
this.iframe.style.zIndex = 9999;
this.iframe.style.width = "100%";
this.iframe.style.height = "100%";
this.iframe.style.position = "absolute";
this.iframe.style.top = window.scrollY + "px";
binded = this.postMessage.bind(this);
window.addEventListener("message", binded, false);
this.iframe.setAttribute('src', drawUrl);
document.body.appendChild(this.iframe);
}
drawio.postMessage = function (evt) {
if (evt.data.length < 1) return
var msg = JSON.parse(evt.data)
var svg = '';
switch (msg.event) {
case "configure":
this.iframe.contentWindow.postMessage(
JSON.stringify({
action: "configure",
config: {
defaultFonts: ["Humor Sans", "Helvetica", "Times New Roman"],
},
}),
"*"
);
break;
case "init":
code = window.sessionStorage.getItem("drawio")
svg = decodeURIComponent(escape(window.atob(code)))
this.iframe.contentWindow.postMessage(
JSON.stringify({ action: "load", autosave: 1, xml: svg }),
"*"
);
break;
case "autosave":
window.sessionStorage.setItem("drawio", svg);
break;
case "save":
this.iframe.contentWindow.postMessage(
JSON.stringify({
action: "export",
format: "xmlsvg",
xml: msg.xml,
spin: "Updating page",
}),
"*"
);
break;
case "export":
svgData = msg.data.substring(msg.data.indexOf(',') + 1);
// clean event bind
window.removeEventListener("message", this.binded);
document.body.removeChild(this.iframe);
// write back svg data
var cm = window.editor.cm;
cm.doc.replaceSelection('```drawio\n' + svgData + '\n```', 'start')
// clean state data
window.sessionStorage.setItem("drawio", '');
break;
case "exit":
window.removeEventListener("message", this.binded);
document.body.removeChild(this.iframe);
break;
}
}
window.editormdLocales = {
'zh-CN': {
@@ -81,8 +225,10 @@ $(function () {
highlightStyle: window.highlightStyle ? window.highlightStyle : "github",
tex: true,
saveHTMLToTextarea: true,
codeFold: true,
onload: function () {
onload: function() {
this.registerHelper()
this.hideToolbar();
var keyMap = {
"Ctrl-S": function (cm) {
@@ -113,12 +259,87 @@ $(function () {
});
window.isLoad = true;
this.tableEditor = TableEditor.initTableEditor(this.cm)
},
onchange: function () {
/**
* 实现画图的事件注入
*
* 1. 分析文本添加点击编辑事件processMarkers
* 2. 获取内容,存储状态数据
* 3. 打开编辑画面
* 4. 推出触发变更事件,并回写数据
*/
var cm = window.editor.cm;
drawio.processMarkers(cm.firstLine(), cm.lastLine() + 1)
resetEditorChanged(true);
}
});
editormd.fn.registerHelper = function () {
const maxDepth = 100
const codeBlockStartMatch = /^`{3}[a-zA-Z0-9]+$/
const codeBlockEndMatch = /^`{3}$/
editormd.$CodeMirror.registerHelper('fold', 'markdown', function (cm, start) {
const firstLine = cm.getLine(start.line)
const lastLineNo = cm.lastLine()
let end
function isHeader(lineNo) {
const tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0))
return tokentype && /\bheader\b/.test(tokentype)
}
function headerLevel(lineNo, line, nextLine) {
let match = line && line.match(/^#+/)
if (match && isHeader(lineNo)) return match[0].length
match = nextLine && nextLine.match(/^[=-]+\s*$/)
if (match && isHeader(lineNo + 1)) return nextLine[0] === '=' ? 1 : 2
return maxDepth
}
// -> CODE BLOCK
if (codeBlockStartMatch.test(cm.getLine(start.line))) {
end = start.line
let nextNextLine = cm.getLine(end + 1)
while (end < lastLineNo) {
if (codeBlockEndMatch.test(nextNextLine)) {
end++
break
}
end++
nextNextLine = cm.getLine(end + 1)
}
} else {
// -> HEADER
let nextLine = cm.getLine(start.line + 1)
const level = headerLevel(start.line, firstLine, nextLine)
if (level === maxDepth) return undefined
end = start.line
let nextNextLine = cm.getLine(end + 2)
while (end < lastLineNo) {
if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break
++end
nextLine = nextNextLine
nextNextLine = cm.getLine(end + 2)
}
}
return {
from: CodeMirror.Pos(start.line, firstLine.length),
to: CodeMirror.Pos(end, cm.getLine(end).length)
}
})
}
function insertToMarkdown(body) {
window.isLoad = true;
window.editor.insertValue(body);
@@ -193,6 +414,21 @@ $(function () {
}
cm.replaceSelection(selectionText.join("\n"));
}
} else if (name === "drawio") {
/**
* TODO: 画图功能实现
*
* 1. 获取光标处数据,存储数据
* 2. 打开画图页面,初始化数据(获取数据)
*/
window.sessionStorage.setItem("drawio", '');
var cm = window.editor.cm;
const selStartLine = cm.getCursor('from').line
const selEndLine = cm.getCursor('to').line + 1
drawio.processMarkers(selStartLine, selEndLine)
drawio.show()
} else {
var action = window.editor.toolbarHandlers[name];
@@ -631,4 +867,4 @@ $(function () {
}).on("shown.bs.modal", function () {
$("#jsonContent").focus();
});
});
});

1
static/table-editor/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

3828
static/table-editor/dist/index.js vendored Normal file

File diff suppressed because it is too large Load Diff

6904
static/table-editor/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
{
"name": "mte",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build:release": "cross-env NODE_ENV=production webpack",
"build:debug": "cross-env NODE_ENV=development webpack",
"build": "npm run build:release",
"clean": "rimraf build"
},
"dependencies": {
"@susisu/mte-kernel": "^1.0.0",
"codemirror": "^5.31.0"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"cpx": "^1.5.0",
"cross-env": "^5.1.1",
"eslint": "^4.11.0",
"npm-run-all": "^4.1.2",
"rimraf": "^2.6.2",
"uglifyjs-webpack-plugin": "^1.1.0",
"webpack": "^3.8.1"
}
}

View File

@@ -0,0 +1,198 @@
/* global CodeMirror, $ */
import { TableEditor, Point, options, Alignment, FormatType } from '@susisu/mte-kernel'
// port of the code from: https://github.com/susisu/mte-demo/blob/master/src/main.js
// text editor interface
// see https://doc.esdoc.org/github.com/susisu/mte-kernel/class/lib/text-editor.js~ITextEditor.html
class TextEditorInterface {
constructor (editor) {
this.editor = editor
this.doc = editor.getDoc()
this.transaction = false
this.onDidFinishTransaction = null
}
getCursorPosition () {
const { line, ch } = this.doc.getCursor()
return new Point(line, ch)
}
setCursorPosition (pos) {
this.doc.setCursor({ line: pos.row, ch: pos.column })
}
setSelectionRange (range) {
this.doc.setSelection(
{ line: range.start.row, ch: range.start.column },
{ line: range.end.row, ch: range.end.column }
)
}
getLastRow () {
return this.doc.lineCount() - 1
}
acceptsTableEdit () {
return true
}
getLine (row) {
return this.doc.getLine(row)
}
insertLine (row, line) {
const lastRow = this.getLastRow()
if (row > lastRow) {
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
'\n' + line,
{ line: lastRow, ch: lastLine.length },
{ line: lastRow, ch: lastLine.length }
)
} else {
this.doc.replaceRange(
line + '\n',
{ line: row, ch: 0 },
{ line: row, ch: 0 }
)
}
}
deleteLine (row) {
const lastRow = this.getLastRow()
if (row >= lastRow) {
if (lastRow > 0) {
const preLastLine = this.getLine(lastRow - 1)
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
'',
{ line: lastRow - 1, ch: preLastLine.length },
{ line: lastRow, ch: lastLine.length }
)
} else {
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
'',
{ line: lastRow, ch: 0 },
{ line: lastRow, ch: lastLine.length }
)
}
} else {
this.doc.replaceRange(
'',
{ line: row, ch: 0 },
{ line: row + 1, ch: 0 }
)
}
}
replaceLines (startRow, endRow, lines) {
const lastRow = this.getLastRow()
if (endRow > lastRow) {
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
lines.join('\n'),
{ line: startRow, ch: 0 },
{ line: lastRow, ch: lastLine.length }
)
} else {
this.doc.replaceRange(
lines.join('\n') + '\n',
{ line: startRow, ch: 0 },
{ line: endRow, ch: 0 }
)
}
}
transact (func) {
this.transaction = true
func()
this.transaction = false
if (this.onDidFinishTransaction) {
this.onDidFinishTransaction.call(undefined)
}
}
}
export function initTableEditor (editor) {
// create an interface to the text editor
const editorIntf = new TextEditorInterface(editor)
// create a table editor object
const tableEditor = new TableEditor(editorIntf)
// options for the table editor
const opts = options({
smartCursor: true,
formatType: FormatType.NORMAL
})
// keymap of the commands
// from https://github.com/susisu/mte-demo/blob/master/src/main.js
const keyMap = CodeMirror.normalizeKeyMap({
Tab: () => { tableEditor.nextCell(opts) },
'Shift-Tab': () => { tableEditor.previousCell(opts) },
Enter: () => { tableEditor.nextRow(opts) },
'Ctrl-Enter': () => { tableEditor.escape(opts) },
'Cmd-Enter': () => { tableEditor.escape(opts) },
'Shift-Ctrl-Left': () => { tableEditor.alignColumn(Alignment.LEFT, opts) },
'Shift-Cmd-Left': () => { tableEditor.alignColumn(Alignment.LEFT, opts) },
'Shift-Ctrl-Right': () => { tableEditor.alignColumn(Alignment.RIGHT, opts) },
'Shift-Cmd-Right': () => { tableEditor.alignColumn(Alignment.RIGHT, opts) },
'Shift-Ctrl-Up': () => { tableEditor.alignColumn(Alignment.CENTER, opts) },
'Shift-Cmd-Up': () => { tableEditor.alignColumn(Alignment.CENTER, opts) },
'Shift-Ctrl-Down': () => { tableEditor.alignColumn(Alignment.NONE, opts) },
'Shift-Cmd-Down': () => { tableEditor.alignColumn(Alignment.NONE, opts) },
'Ctrl-Left': () => { tableEditor.moveFocus(0, -1, opts) },
'Cmd-Left': () => { tableEditor.moveFocus(0, -1, opts) },
'Ctrl-Right': () => { tableEditor.moveFocus(0, 1, opts) },
'Cmd-Right': () => { tableEditor.moveFocus(0, 1, opts) },
'Ctrl-Up': () => { tableEditor.moveFocus(-1, 0, opts) },
'Cmd-Up': () => { tableEditor.moveFocus(-1, 0, opts) },
'Ctrl-Down': () => { tableEditor.moveFocus(1, 0, opts) },
'Cmd-Down': () => { tableEditor.moveFocus(1, 0, opts) },
'Ctrl-K Ctrl-I': () => { tableEditor.insertRow(opts) },
'Cmd-K Cmd-I': () => { tableEditor.insertRow(opts) },
'Ctrl-L Ctrl-I': () => { tableEditor.deleteRow(opts) },
'Cmd-L Cmd-I': () => { tableEditor.deleteRow(opts) },
'Ctrl-K Ctrl-J': () => { tableEditor.insertColumn(opts) },
'Cmd-K Cmd-J': () => { tableEditor.insertColumn(opts) },
'Ctrl-L Ctrl-J': () => { tableEditor.deleteColumn(opts) },
'Cmd-L Cmd-J': () => { tableEditor.deleteColumn(opts) },
'Alt-Shift-Ctrl-Left': () => { tableEditor.moveColumn(-1, opts) },
'Alt-Shift-Cmd-Left': () => { tableEditor.moveColumn(-1, opts) },
'Alt-Shift-Ctrl-Right': () => { tableEditor.moveColumn(1, opts) },
'Alt-Shift-Cmd-Right': () => { tableEditor.moveColumn(1, opts) },
'Alt-Shift-Ctrl-Up': () => { tableEditor.moveRow(-1, opts) },
'Alt-Shift-Cmd-Up': () => { tableEditor.moveRow(-1, opts) },
'Alt-Shift-Ctrl-Down': () => { tableEditor.moveRow(1, opts) },
'Alt-Shift-Cmd-Down': () => { tableEditor.moveRow(1, opts) }
})
// enable keymap if the cursor is in a table
function updateActiveState() {
const active = tableEditor.cursorIsInTable();
if (active) {
editor.setOption("extraKeys", keyMap);
}
else {
editor.setOption("extraKeys", null);
tableEditor.resetSmartCursor();
}
}
// event subscriptions
editor.on("cursorActivity", () => {
if (!editorIntf.transaction) {
updateActiveState();
}
});
editor.on("changes", () => {
if (!editorIntf.transaction) {
updateActiveState();
}
});
editorIntf.onDidFinishTransaction = () => {
updateActiveState();
};
return tableEditor
}

View File

@@ -0,0 +1,15 @@
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/main.js', // 库的入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录
filename: 'index.js', // 输出文件名称
library: 'TableEditor', // 库的全局变量名
libraryTarget: 'umd', // 输出库的目标格式
umdNamedDefine: true // 对UMD模块进行命名定义
},
// 配置其他选项...
};