@jx3box/jx3box-editor 3.2.2 → 3.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/config/global.less
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jx3box/jx3box-editor",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.4",
|
|
4
4
|
"description": "JX3BOX Article & Editor",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@element-plus/icons-vue": "^2.0.10",
|
|
17
17
|
"@imengyu/vue3-context-menu": "^1.5.4",
|
|
18
|
-
"@jx3box/jx3box-common": "^9.2.
|
|
19
|
-
"@jx3box/jx3box-data": "^3.9.
|
|
20
|
-
"@jx3box/jx3box-emotion": "^1.3.
|
|
21
|
-
"@jx3box/jx3box-macro": "^1.0.
|
|
22
|
-
"@jx3box/jx3box-talent": "^1.3.
|
|
18
|
+
"@jx3box/jx3box-common": "^9.2.1",
|
|
19
|
+
"@jx3box/jx3box-data": "^3.9.4",
|
|
20
|
+
"@jx3box/jx3box-emotion": "^1.3.0",
|
|
21
|
+
"@jx3box/jx3box-macro": "^1.0.3",
|
|
22
|
+
"@jx3box/jx3box-talent": "^1.3.13",
|
|
23
23
|
"@tinymce/tinymce-vue": "^5.0.0",
|
|
24
24
|
"axios": "^0.19.2",
|
|
25
25
|
"cheerio": "^1.1.2",
|
|
@@ -89,6 +89,6 @@
|
|
|
89
89
|
},
|
|
90
90
|
"repository": {
|
|
91
91
|
"type": "git",
|
|
92
|
-
"url": "git+https://github.com/
|
|
92
|
+
"url": "git+https://github.com/JX3BOX/jx3box-editor.git"
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -32,12 +32,11 @@
|
|
|
32
32
|
|
|
33
33
|
h4 {
|
|
34
34
|
font-size: 15px;
|
|
35
|
-
padding: 8px 10px;
|
|
35
|
+
padding: 8px 10px 8px 20px;
|
|
36
36
|
|
|
37
37
|
background-color: #fafbfc;
|
|
38
38
|
border-radius: 6px;
|
|
39
|
-
border-left:4px solid @primary;
|
|
40
|
-
|
|
39
|
+
// border-left:4px solid @primary;
|
|
41
40
|
|
|
42
41
|
position: relative;
|
|
43
42
|
overflow: hidden;
|
|
@@ -45,17 +44,39 @@
|
|
|
45
44
|
// border: 1px solid @primary;
|
|
46
45
|
&::before {
|
|
47
46
|
content: "";
|
|
48
|
-
font-size: 20px;
|
|
49
47
|
display: block;
|
|
50
48
|
position: absolute;
|
|
51
|
-
width:
|
|
52
|
-
height:
|
|
53
|
-
top:
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
width: 5px;
|
|
50
|
+
height: 60%;
|
|
51
|
+
top: 50%;
|
|
52
|
+
transform: translateY(-50%);
|
|
53
|
+
left: 10px;
|
|
54
|
+
background-color: @primary;
|
|
55
|
+
border-radius: 10px;
|
|
56
|
+
transition: all 0.2s ease;
|
|
56
57
|
// border-bottom-right-radius: 20px;
|
|
57
58
|
// border-bottom-left-radius: 20px;
|
|
58
59
|
}
|
|
60
|
+
// &::after {
|
|
61
|
+
// content: "";
|
|
62
|
+
// display: block;
|
|
63
|
+
// position: absolute;
|
|
64
|
+
// width: 5px;
|
|
65
|
+
// height: 60%;
|
|
66
|
+
// top: 50%;
|
|
67
|
+
// transform: translateY(-50%);
|
|
68
|
+
// left: 10px;
|
|
69
|
+
// background-color: @v4yellow;
|
|
70
|
+
// border-radius: 10px;
|
|
71
|
+
// z-index: 2;
|
|
72
|
+
// display: none;
|
|
73
|
+
// }
|
|
74
|
+
|
|
75
|
+
&:hover {
|
|
76
|
+
&::before {
|
|
77
|
+
background-color: @v4yellow;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
59
80
|
}
|
|
60
81
|
|
|
61
82
|
h5 {
|
|
@@ -74,6 +95,11 @@
|
|
|
74
95
|
bottom: -1px;
|
|
75
96
|
left: 0;
|
|
76
97
|
}
|
|
98
|
+
&:hover{
|
|
99
|
+
&::after {
|
|
100
|
+
background-color: @v4yellow;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
h6 {
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
line-height:2.2;
|
|
13
13
|
color:@color;
|
|
14
14
|
|
|
15
|
-
td,
|
|
16
|
-
th {
|
|
15
|
+
td,th {
|
|
17
16
|
padding: 6px 10px;
|
|
18
|
-
}
|
|
19
|
-
td {
|
|
20
17
|
border: 1px solid #eee;
|
|
21
18
|
}
|
|
22
19
|
th {
|
|
23
20
|
background-color: #fafbfc;
|
|
24
21
|
font-weight: 500;
|
|
25
22
|
}
|
|
23
|
+
tr{
|
|
24
|
+
background-color:#fff;
|
|
25
|
+
}
|
|
26
26
|
tr:nth-child(2n + 1) {
|
|
27
27
|
background-color: #fafbfc;
|
|
28
28
|
}
|
package/src/assets/js/katex.js
CHANGED
|
@@ -2,6 +2,181 @@ import $ from 'jquery';
|
|
|
2
2
|
import katex from 'katex';
|
|
3
3
|
import 'katex/dist/katex.min.css';
|
|
4
4
|
|
|
5
|
+
function isEscaped(text, index) {
|
|
6
|
+
let count = 0;
|
|
7
|
+
for (let i = index - 1; i >= 0 && text[i] === '\\'; i--) {
|
|
8
|
+
count++;
|
|
9
|
+
}
|
|
10
|
+
return count % 2 === 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function findUnescaped(text, token, start) {
|
|
14
|
+
let index = start;
|
|
15
|
+
while (index < text.length) {
|
|
16
|
+
const found = text.indexOf(token, index);
|
|
17
|
+
if (found === -1) return -1;
|
|
18
|
+
if (!isEscaped(text, found)) return found;
|
|
19
|
+
index = found + token.length;
|
|
20
|
+
}
|
|
21
|
+
return -1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getTextNodes(container, shouldAccept) {
|
|
25
|
+
const walker = document.createTreeWalker(
|
|
26
|
+
container,
|
|
27
|
+
NodeFilter.SHOW_TEXT,
|
|
28
|
+
{
|
|
29
|
+
acceptNode: function (node) {
|
|
30
|
+
// 跳过已渲染的节点
|
|
31
|
+
if (
|
|
32
|
+
node.parentNode &&
|
|
33
|
+
(node.parentNode.classList?.contains('katex') ||
|
|
34
|
+
node.parentNode.closest("pre, code, .katex"))
|
|
35
|
+
) {
|
|
36
|
+
return NodeFilter.FILTER_REJECT;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return shouldAccept(node.nodeValue || '') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const nodesToReplace = [];
|
|
45
|
+
while (walker.nextNode()) {
|
|
46
|
+
nodesToReplace.push(walker.currentNode);
|
|
47
|
+
}
|
|
48
|
+
return nodesToReplace;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function collectInlineMatches(text) {
|
|
52
|
+
const matches = [];
|
|
53
|
+
let index = 0;
|
|
54
|
+
|
|
55
|
+
while (index < text.length) {
|
|
56
|
+
const parenStart = text.indexOf('\\(', index);
|
|
57
|
+
const dollarStart = text.indexOf('$', index);
|
|
58
|
+
const candidates = [parenStart, dollarStart].filter((value) => value !== -1 && !isEscaped(text, value));
|
|
59
|
+
const start = candidates.length ? Math.min(...candidates) : -1;
|
|
60
|
+
if (start === -1) break;
|
|
61
|
+
|
|
62
|
+
if (text.slice(start, start + 2) === '\\(') {
|
|
63
|
+
const end = findUnescaped(text, '\\)', start + 2);
|
|
64
|
+
if (end !== -1) {
|
|
65
|
+
const raw = text.slice(start + 2, end);
|
|
66
|
+
if (raw && !raw.includes('\n')) {
|
|
67
|
+
matches.push({
|
|
68
|
+
start,
|
|
69
|
+
end: end + 2,
|
|
70
|
+
raw,
|
|
71
|
+
full: text.slice(start, end + 2),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
index = end + 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
} else if (text[start] === '$' && text[start + 1] !== '$') {
|
|
78
|
+
const end = findUnescaped(text, '$', start + 1);
|
|
79
|
+
if (end !== -1 && text[end + 1] !== '$') {
|
|
80
|
+
const raw = text.slice(start + 1, end);
|
|
81
|
+
if (raw && !raw.includes('\n')) {
|
|
82
|
+
matches.push({
|
|
83
|
+
start,
|
|
84
|
+
end: end + 1,
|
|
85
|
+
raw,
|
|
86
|
+
full: text.slice(start, end + 1),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
index = end + 1;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
index = start + 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return matches;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function collectBlockMatches(text) {
|
|
101
|
+
const matches = [];
|
|
102
|
+
let index = 0;
|
|
103
|
+
|
|
104
|
+
while (index < text.length) {
|
|
105
|
+
const dollarStart = text.indexOf('$$', index);
|
|
106
|
+
const bracketStart = text.indexOf('\\[', index);
|
|
107
|
+
const candidates = [dollarStart, bracketStart].filter((value) => value !== -1 && !isEscaped(text, value));
|
|
108
|
+
const start = candidates.length ? Math.min(...candidates) : -1;
|
|
109
|
+
if (start === -1) break;
|
|
110
|
+
|
|
111
|
+
if (text.slice(start, start + 2) === '$$') {
|
|
112
|
+
const end = findUnescaped(text, '$$', start + 2);
|
|
113
|
+
if (end !== -1) {
|
|
114
|
+
const raw = text.slice(start + 2, end).trim();
|
|
115
|
+
if (raw) {
|
|
116
|
+
matches.push({
|
|
117
|
+
start,
|
|
118
|
+
end: end + 2,
|
|
119
|
+
raw,
|
|
120
|
+
full: text.slice(start, end + 2),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
index = end + 2;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
} else if (text.slice(start, start + 2) === '\\[') {
|
|
127
|
+
const end = findUnescaped(text, '\\]', start + 2);
|
|
128
|
+
if (end !== -1) {
|
|
129
|
+
const raw = text.slice(start + 2, end).trim();
|
|
130
|
+
if (raw) {
|
|
131
|
+
matches.push({
|
|
132
|
+
start,
|
|
133
|
+
end: end + 2,
|
|
134
|
+
raw,
|
|
135
|
+
full: text.slice(start, end + 2),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
index = end + 2;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
index = start + 1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return matches;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function replaceTextNode(node, matches, renderMatch, logPrefix) {
|
|
150
|
+
if (!matches.length) return;
|
|
151
|
+
|
|
152
|
+
const text = node.nodeValue || '';
|
|
153
|
+
const frag = document.createDocumentFragment();
|
|
154
|
+
let lastIndex = 0;
|
|
155
|
+
|
|
156
|
+
matches.forEach((match) => {
|
|
157
|
+
if (match.start > lastIndex) {
|
|
158
|
+
frag.appendChild(document.createTextNode(text.slice(lastIndex, match.start)));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
frag.appendChild(renderMatch(match.raw));
|
|
163
|
+
} catch (e) {
|
|
164
|
+
frag.appendChild(document.createTextNode(match.full));
|
|
165
|
+
console.error(logPrefix, match.raw, e.message);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
lastIndex = match.end;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (lastIndex < text.length) {
|
|
172
|
+
frag.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (frag.hasChildNodes()) {
|
|
176
|
+
node.parentNode.replaceChild(frag, node);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
5
180
|
function renderKatexBlock(selector = ".w-latex") {
|
|
6
181
|
try {
|
|
7
182
|
$(selector).each(function() {
|
|
@@ -39,60 +214,11 @@ function renderKatexBlock(selector = ".w-latex") {
|
|
|
39
214
|
}
|
|
40
215
|
|
|
41
216
|
function renderKatexInline(container = document.body) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
NodeFilter.SHOW_TEXT,
|
|
48
|
-
{
|
|
49
|
-
acceptNode: function (node) {
|
|
50
|
-
// 跳过已渲染的节点
|
|
51
|
-
if (
|
|
52
|
-
node.parentNode &&
|
|
53
|
-
(node.parentNode.classList?.contains('katex') ||
|
|
54
|
-
node.parentNode.closest("pre, code, .katex"))
|
|
55
|
-
) {
|
|
56
|
-
return NodeFilter.FILTER_REJECT;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const value = node.nodeValue || '';
|
|
60
|
-
if (value.includes("\\(") || value.includes("$")) {
|
|
61
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
62
|
-
}
|
|
63
|
-
return NodeFilter.FILTER_REJECT;
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const nodesToReplace = [];
|
|
69
|
-
while (walker.nextNode()) {
|
|
70
|
-
nodesToReplace.push(walker.currentNode);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
nodesToReplace.forEach((node) => {
|
|
74
|
-
const text = node.nodeValue;
|
|
75
|
-
const frag = document.createDocumentFragment();
|
|
76
|
-
let lastIndex = 0;
|
|
77
|
-
|
|
78
|
-
// 重置正则状态
|
|
79
|
-
inlineRegex.lastIndex = 0;
|
|
80
|
-
|
|
81
|
-
const matches = [...text.matchAll(inlineRegex)];
|
|
82
|
-
|
|
83
|
-
matches.forEach((match) => {
|
|
84
|
-
const fullMatch = match[0];
|
|
85
|
-
const parenContent = match[2];
|
|
86
|
-
const dollarContent = match[3];
|
|
87
|
-
const raw = parenContent || dollarContent;
|
|
88
|
-
const matchStart = match.index;
|
|
89
|
-
|
|
90
|
-
// 添加匹配前的文本
|
|
91
|
-
if (matchStart > lastIndex) {
|
|
92
|
-
frag.appendChild(document.createTextNode(text.slice(lastIndex, matchStart)));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
try {
|
|
217
|
+
getTextNodes(container, (value) => value.includes("\\(") || value.includes("$")).forEach((node) => {
|
|
218
|
+
replaceTextNode(
|
|
219
|
+
node,
|
|
220
|
+
collectInlineMatches(node.nodeValue || ''),
|
|
221
|
+
(raw) => {
|
|
96
222
|
const span = document.createElement("span");
|
|
97
223
|
span.className = "katex-inline";
|
|
98
224
|
span.innerHTML = katex.renderToString(raw, {
|
|
@@ -101,81 +227,19 @@ function renderKatexInline(container = document.body) {
|
|
|
101
227
|
strict: false,
|
|
102
228
|
trust: true
|
|
103
229
|
});
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
lastIndex = matchStart + fullMatch.length;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// 添加剩余文本
|
|
114
|
-
if (lastIndex < text.length) {
|
|
115
|
-
frag.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (frag.hasChildNodes()) {
|
|
119
|
-
node.parentNode.replaceChild(frag, node);
|
|
120
|
-
}
|
|
230
|
+
return span;
|
|
231
|
+
},
|
|
232
|
+
"Inline render error:"
|
|
233
|
+
);
|
|
121
234
|
});
|
|
122
235
|
}
|
|
123
236
|
|
|
124
237
|
function renderKatexDisplayBlock(container = document.body) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
NodeFilter.SHOW_TEXT,
|
|
131
|
-
{
|
|
132
|
-
acceptNode: function (node) {
|
|
133
|
-
// 跳过已渲染的节点
|
|
134
|
-
if (
|
|
135
|
-
node.parentNode &&
|
|
136
|
-
(node.parentNode.classList?.contains('katex') ||
|
|
137
|
-
node.parentNode.closest("pre, code, .katex"))
|
|
138
|
-
) {
|
|
139
|
-
return NodeFilter.FILTER_REJECT;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const value = node.nodeValue || '';
|
|
143
|
-
if (value.includes("$$") || value.includes("\\[")) {
|
|
144
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
145
|
-
}
|
|
146
|
-
return NodeFilter.FILTER_REJECT;
|
|
147
|
-
},
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const nodesToReplace = [];
|
|
152
|
-
while (walker.nextNode()) {
|
|
153
|
-
nodesToReplace.push(walker.currentNode);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
nodesToReplace.forEach((node) => {
|
|
157
|
-
const text = node.nodeValue;
|
|
158
|
-
const frag = document.createDocumentFragment();
|
|
159
|
-
let lastIndex = 0;
|
|
160
|
-
|
|
161
|
-
// 重置正则状态
|
|
162
|
-
blockRegex.lastIndex = 0;
|
|
163
|
-
|
|
164
|
-
const matches = [...text.matchAll(blockRegex)];
|
|
165
|
-
|
|
166
|
-
matches.forEach((match) => {
|
|
167
|
-
const fullMatch = match[0];
|
|
168
|
-
const dollarContent = match[2];
|
|
169
|
-
const bracketContent = match[3];
|
|
170
|
-
const raw = (dollarContent || bracketContent).trim();
|
|
171
|
-
const matchStart = match.index;
|
|
172
|
-
|
|
173
|
-
// 添加匹配前的文本
|
|
174
|
-
if (matchStart > lastIndex) {
|
|
175
|
-
frag.appendChild(document.createTextNode(text.slice(lastIndex, matchStart)));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
try {
|
|
238
|
+
getTextNodes(container, (value) => value.includes("$$") || value.includes("\\[")).forEach((node) => {
|
|
239
|
+
replaceTextNode(
|
|
240
|
+
node,
|
|
241
|
+
collectBlockMatches(node.nodeValue || ''),
|
|
242
|
+
(raw) => {
|
|
179
243
|
const div = document.createElement("div");
|
|
180
244
|
div.className = "katex-block";
|
|
181
245
|
div.innerHTML = katex.renderToString(raw, {
|
|
@@ -184,23 +248,10 @@ function renderKatexDisplayBlock(container = document.body) {
|
|
|
184
248
|
strict: false,
|
|
185
249
|
trust: true
|
|
186
250
|
});
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
lastIndex = matchStart + fullMatch.length;
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// 添加剩余文本
|
|
197
|
-
if (lastIndex < text.length) {
|
|
198
|
-
frag.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (frag.hasChildNodes()) {
|
|
202
|
-
node.parentNode.replaceChild(frag, node);
|
|
203
|
-
}
|
|
251
|
+
return div;
|
|
252
|
+
},
|
|
253
|
+
"Block render error:"
|
|
254
|
+
);
|
|
204
255
|
});
|
|
205
256
|
}
|
|
206
257
|
|