lakelib 0.1.15 → 0.1.17
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/README.md +12 -2
- package/dist/lake.css +155 -5
- package/dist/lake.min.js +38 -31
- package/dist/lake.min.js.map +1 -1
- package/lib/lake.css +155 -5
- package/lib/lake.js +338 -108
- package/lib/lake.js.map +1 -1
- package/lib/types/boxes/equation.d.ts +2 -0
- package/lib/types/css/index.d.ts +2 -0
- package/lib/types/editor.d.ts +4 -5
- package/lib/types/i18n/en-US/index.d.ts +5 -0
- package/lib/types/i18n/ja/index.d.ts +5 -0
- package/lib/types/i18n/ko/index.d.ts +5 -0
- package/lib/types/i18n/types.d.ts +28 -0
- package/lib/types/i18n/zh-CN/index.d.ts +5 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/models/box.d.ts +1 -2
- package/lib/types/models/fragment.d.ts +1 -2
- package/lib/types/models/nodes.d.ts +11 -14
- package/lib/types/models/range.d.ts +1 -1
- package/lib/types/operations/insert-node.d.ts +1 -2
- package/lib/types/parsers/html-parser.d.ts +0 -1
- package/lib/types/plugins/equation.d.ts +3 -0
- package/lib/types/plugins/special-character.d.ts +3 -0
- package/lib/types/types/commands.d.ts +20 -0
- package/lib/types/types/dropdown.d.ts +2 -1
- package/lib/types/types/native.d.ts +0 -23
- package/lib/types/ui/commands-popup.d.ts +24 -0
- package/lib/types/ui/toolbar.d.ts +1 -2
- package/lib/types/utils/from-base64.d.ts +1 -0
- package/lib/types/utils/get-box.d.ts +1 -2
- package/lib/types/utils/index.d.ts +2 -0
- package/lib/types/utils/query.d.ts +1 -2
- package/lib/types/utils/to-base64.d.ts +1 -0
- package/lib/types/utils/to-node-list.d.ts +1 -2
- package/package.json +20 -20
package/lib/lake.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { Base64 } from 'js-base64';
|
|
2
1
|
import EventEmitter from 'eventemitter3';
|
|
3
2
|
import { i18nObject as i18nObject$1 } from 'typesafe-i18n';
|
|
4
|
-
import debounce from '
|
|
5
|
-
import isEqual from '
|
|
6
|
-
import
|
|
7
|
-
import { createKeybindingsHandler } from 'tinykeys';
|
|
3
|
+
import debounce from 'debounce';
|
|
4
|
+
import isEqual from 'fast-deep-equal/es6';
|
|
5
|
+
import { isKeyHotkey } from 'is-hotkey';
|
|
8
6
|
import 'photoswipe/style.css';
|
|
9
7
|
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
|
10
8
|
import PhotoSwipe from 'photoswipe';
|
|
@@ -145,6 +143,10 @@ var attachment = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=
|
|
|
145
143
|
|
|
146
144
|
var emoji$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216ZM80,108a12,12,0,1,1,12,12A12,12,0,0,1,80,108Zm96,0a12,12,0,1,1-12-12A12,12,0,0,1,176,108Zm-1.07,48c-10.29,17.79-27.4,28-46.93,28s-36.63-10.2-46.92-28a8,8,0,1,1,13.84-8c7.47,12.91,19.21,20,33.08,20s25.61-7.1,33.07-20a8,8,0,0,1,13.86,8Z\"></path></svg>";
|
|
147
145
|
|
|
146
|
+
var specialCharacter$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 32 32\"><path d=\"M22.7373,25A14.3093,14.3093,0,0,0,27,15C27,8.42,22.58,4,16,4S5,8.42,5,15A14.3093,14.3093,0,0,0,9.2627,25H4v2h8V24.7617A12.5683,12.5683,0,0,1,7,15c0-5.4673,3.5327-9,9-9s9,3.5327,9,9a12.5683,12.5683,0,0,1-5,9.7617V27h8V25Z\"/></svg>";
|
|
147
|
+
|
|
148
|
+
var equation$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M184,72V56H80.65l53.6,67a8,8,0,0,1,0,10l-53.6,67H184V184a8,8,0,0,1,16,0v24a8,8,0,0,1-8,8H64a8,8,0,0,1-6.25-13l60-75-60-75A8,8,0,0,1,64,40H192a8,8,0,0,1,8,8V72a8,8,0,0,1-16,0Z\"></path></svg>";
|
|
149
|
+
|
|
148
150
|
var table = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM40,112H80v32H40Zm56,0H216v32H96ZM216,64V96H40V64ZM40,160H80v32H40Zm176,32H96V160H216v32Z\"></path></svg>";
|
|
149
151
|
|
|
150
152
|
// These icons are sourced from open source projects.
|
|
@@ -224,6 +226,8 @@ const icons = new Map([
|
|
|
224
226
|
['video', video$1],
|
|
225
227
|
['attachment', attachment],
|
|
226
228
|
['emoji', emoji$1],
|
|
229
|
+
['specialCharacter', specialCharacter$1],
|
|
230
|
+
['equation', equation$1],
|
|
227
231
|
['table', table],
|
|
228
232
|
]);
|
|
229
233
|
|
|
@@ -287,6 +291,28 @@ function inString(string, value, delimiter) {
|
|
|
287
291
|
return (delimiter + string + delimiter).indexOf(delimiter + value + delimiter) >= 0;
|
|
288
292
|
}
|
|
289
293
|
|
|
294
|
+
// Creates a Base64-encoded ASCII string from a string.
|
|
295
|
+
function toBase64(value) {
|
|
296
|
+
const encoder = new TextEncoder();
|
|
297
|
+
const byteArray = encoder.encode(value);
|
|
298
|
+
let binaryString = '';
|
|
299
|
+
byteArray.forEach(byte => {
|
|
300
|
+
binaryString += String.fromCharCode(byte);
|
|
301
|
+
});
|
|
302
|
+
return window.btoa(binaryString);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Decodes a string of data which has been encoded using Base64 encoding.
|
|
306
|
+
function fromBase64(value) {
|
|
307
|
+
const binaryString = window.atob(value);
|
|
308
|
+
const byteArray = new Uint8Array(binaryString.length);
|
|
309
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
310
|
+
byteArray[i] = binaryString.charCodeAt(i);
|
|
311
|
+
}
|
|
312
|
+
const decoder = new TextDecoder();
|
|
313
|
+
return decoder.decode(byteArray);
|
|
314
|
+
}
|
|
315
|
+
|
|
290
316
|
// Converts an alpha value to a hex value.
|
|
291
317
|
function alphaToHex(value) {
|
|
292
318
|
const hexString = Math.round(Number.parseFloat(value) * 255).toString(16);
|
|
@@ -405,8 +431,6 @@ function toNodeList(content, valueType) {
|
|
|
405
431
|
return nodeList;
|
|
406
432
|
}
|
|
407
433
|
|
|
408
|
-
const NativeNode = Node;
|
|
409
|
-
|
|
410
434
|
const blockTagNames = new Set([
|
|
411
435
|
'h1',
|
|
412
436
|
'h2',
|
|
@@ -515,7 +539,7 @@ class Nodes {
|
|
|
515
539
|
return false;
|
|
516
540
|
}
|
|
517
541
|
const node = this.get(0);
|
|
518
|
-
return node.nodeType ===
|
|
542
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
519
543
|
}
|
|
520
544
|
// Returns a boolean value indicating whether the node is a text node.
|
|
521
545
|
get isText() {
|
|
@@ -523,7 +547,7 @@ class Nodes {
|
|
|
523
547
|
return false;
|
|
524
548
|
}
|
|
525
549
|
const node = this.get(0);
|
|
526
|
-
return node.nodeType ===
|
|
550
|
+
return node.nodeType === Node.TEXT_NODE;
|
|
527
551
|
}
|
|
528
552
|
// Returns a boolean value indicating whether the node is a block element.
|
|
529
553
|
get isBlock() {
|
|
@@ -679,7 +703,7 @@ class Nodes {
|
|
|
679
703
|
eachElement(callback) {
|
|
680
704
|
const nodes = this.getAll();
|
|
681
705
|
for (let i = 0; i < nodes.length; i++) {
|
|
682
|
-
if (nodes[i].nodeType ===
|
|
706
|
+
if (nodes[i].nodeType === Node.ELEMENT_NODE) {
|
|
683
707
|
if (callback(nodes[i], i) === false) {
|
|
684
708
|
return this;
|
|
685
709
|
}
|
|
@@ -1214,7 +1238,7 @@ class Nodes {
|
|
|
1214
1238
|
return `node (${node.lakeId}): ${nodeValue}`;
|
|
1215
1239
|
}
|
|
1216
1240
|
// Prints information of the first node.
|
|
1217
|
-
|
|
1241
|
+
info() {
|
|
1218
1242
|
debug(this.toString());
|
|
1219
1243
|
}
|
|
1220
1244
|
}
|
|
@@ -1355,12 +1379,12 @@ class Range {
|
|
|
1355
1379
|
reference = 'right';
|
|
1356
1380
|
}
|
|
1357
1381
|
}
|
|
1358
|
-
else {
|
|
1382
|
+
else if (range.startNode.isText) {
|
|
1359
1383
|
const text = range.startNode.text();
|
|
1360
1384
|
if (range.startOffset < text.length) {
|
|
1361
1385
|
range.setEnd(range.startNode, range.startOffset + 1);
|
|
1362
1386
|
}
|
|
1363
|
-
else {
|
|
1387
|
+
else if (range.startOffset > 0) {
|
|
1364
1388
|
range.setStart(range.startNode, range.startOffset - 1);
|
|
1365
1389
|
reference = 'right';
|
|
1366
1390
|
}
|
|
@@ -1813,7 +1837,7 @@ class Range {
|
|
|
1813
1837
|
return this.range.cloneContents();
|
|
1814
1838
|
}
|
|
1815
1839
|
// Prints information of the range.
|
|
1816
|
-
|
|
1840
|
+
info() {
|
|
1817
1841
|
debug('--- range information ---');
|
|
1818
1842
|
debug('start node:', this.startNode.toString(), ', offset:', this.startOffset);
|
|
1819
1843
|
debug('end node:', this.endNode.toString(), ', offset:', this.endOffset);
|
|
@@ -2851,6 +2875,7 @@ var enUS = {
|
|
|
2851
2875
|
image: 'Image',
|
|
2852
2876
|
file: 'File',
|
|
2853
2877
|
emoji: 'Emoji',
|
|
2878
|
+
equation: 'Equation',
|
|
2854
2879
|
removeColor: 'Remove color',
|
|
2855
2880
|
},
|
|
2856
2881
|
link: {
|
|
@@ -2886,6 +2911,10 @@ var enUS = {
|
|
|
2886
2911
|
codeBlock: {
|
|
2887
2912
|
langType: 'Select language',
|
|
2888
2913
|
},
|
|
2914
|
+
equation: {
|
|
2915
|
+
save: 'Done',
|
|
2916
|
+
placeholder: 'Type a TeX expression',
|
|
2917
|
+
},
|
|
2889
2918
|
};
|
|
2890
2919
|
|
|
2891
2920
|
var zhCN = {
|
|
@@ -2935,6 +2964,7 @@ var zhCN = {
|
|
|
2935
2964
|
image: '图片',
|
|
2936
2965
|
file: '文件',
|
|
2937
2966
|
emoji: '表情',
|
|
2967
|
+
equation: '公式',
|
|
2938
2968
|
removeColor: '默认',
|
|
2939
2969
|
},
|
|
2940
2970
|
link: {
|
|
@@ -2970,6 +3000,10 @@ var zhCN = {
|
|
|
2970
3000
|
codeBlock: {
|
|
2971
3001
|
langType: '选择代码语言',
|
|
2972
3002
|
},
|
|
3003
|
+
equation: {
|
|
3004
|
+
save: '确定',
|
|
3005
|
+
placeholder: '请输入 TeX 公式',
|
|
3006
|
+
},
|
|
2973
3007
|
};
|
|
2974
3008
|
|
|
2975
3009
|
var ja = {
|
|
@@ -3019,6 +3053,7 @@ var ja = {
|
|
|
3019
3053
|
image: '画像',
|
|
3020
3054
|
file: 'ファイル',
|
|
3021
3055
|
emoji: '絵文字',
|
|
3056
|
+
equation: '公式',
|
|
3022
3057
|
removeColor: 'デフォルト',
|
|
3023
3058
|
},
|
|
3024
3059
|
link: {
|
|
@@ -3054,6 +3089,10 @@ var ja = {
|
|
|
3054
3089
|
codeBlock: {
|
|
3055
3090
|
langType: 'コード言語を選択',
|
|
3056
3091
|
},
|
|
3092
|
+
equation: {
|
|
3093
|
+
save: '確認',
|
|
3094
|
+
placeholder: 'TeX 公式を入力してください。',
|
|
3095
|
+
},
|
|
3057
3096
|
};
|
|
3058
3097
|
|
|
3059
3098
|
var ko = {
|
|
@@ -3103,6 +3142,7 @@ var ko = {
|
|
|
3103
3142
|
image: '이미지',
|
|
3104
3143
|
file: '파일',
|
|
3105
3144
|
emoji: '이모지',
|
|
3145
|
+
equation: '수식',
|
|
3106
3146
|
removeColor: '기본색',
|
|
3107
3147
|
},
|
|
3108
3148
|
link: {
|
|
@@ -3138,6 +3178,10 @@ var ko = {
|
|
|
3138
3178
|
codeBlock: {
|
|
3139
3179
|
langType: '코드언어 선택',
|
|
3140
3180
|
},
|
|
3181
|
+
equation: {
|
|
3182
|
+
save: '확인',
|
|
3183
|
+
placeholder: 'TeX 수식을 입력하세요',
|
|
3184
|
+
},
|
|
3141
3185
|
};
|
|
3142
3186
|
|
|
3143
3187
|
const localeTranslations = {
|
|
@@ -3204,11 +3248,11 @@ class Dropdown {
|
|
|
3204
3248
|
if (value === '') {
|
|
3205
3249
|
return [];
|
|
3206
3250
|
}
|
|
3207
|
-
return JSON.parse(
|
|
3251
|
+
return JSON.parse(fromBase64(value));
|
|
3208
3252
|
}
|
|
3209
3253
|
// Updates the value of the node.
|
|
3210
3254
|
static setValue(node, value) {
|
|
3211
|
-
node.attr('value',
|
|
3255
|
+
node.attr('value', toBase64(JSON.stringify(value)));
|
|
3212
3256
|
}
|
|
3213
3257
|
static getMenuMap(menuItems, locale) {
|
|
3214
3258
|
const menuMap = new Map();
|
|
@@ -3241,7 +3285,11 @@ class Dropdown {
|
|
|
3241
3285
|
`;
|
|
3242
3286
|
const listNode = query(listContent);
|
|
3243
3287
|
menuNode.append(listNode);
|
|
3244
|
-
if (config.menuType === '
|
|
3288
|
+
if (config.menuType === 'character') {
|
|
3289
|
+
listNode.attr('title', menuText);
|
|
3290
|
+
listNode.find('.lake-dropdown-menu-text').text(menuItem.value);
|
|
3291
|
+
}
|
|
3292
|
+
else if (config.menuType === 'color') {
|
|
3245
3293
|
listNode.attr('title', menuText);
|
|
3246
3294
|
listNode.find('.lake-dropdown-menu-text').css('background-color', menuItem.value);
|
|
3247
3295
|
}
|
|
@@ -3357,7 +3405,11 @@ class Dropdown {
|
|
|
3357
3405
|
});
|
|
3358
3406
|
menuNode.on('click', event => {
|
|
3359
3407
|
event.preventDefault();
|
|
3408
|
+
event.stopPropagation();
|
|
3360
3409
|
const listItem = query(event.target).closest('li');
|
|
3410
|
+
if (listItem.length === 0) {
|
|
3411
|
+
return;
|
|
3412
|
+
}
|
|
3361
3413
|
const value = listItem.attr('value');
|
|
3362
3414
|
Dropdown.setValue(dropdownNode, [value]);
|
|
3363
3415
|
if (textNode.length > 0) {
|
|
@@ -3403,6 +3455,10 @@ class Dropdown {
|
|
|
3403
3455
|
if (config.menuWidth) {
|
|
3404
3456
|
menuNode.css('width', config.menuWidth);
|
|
3405
3457
|
}
|
|
3458
|
+
if (config.menuHeight) {
|
|
3459
|
+
menuNode.addClass('lake-dropdown-menu-with-scroll');
|
|
3460
|
+
menuNode.css('height', config.menuHeight);
|
|
3461
|
+
}
|
|
3406
3462
|
Dropdown.setValue(dropdownNode, [defaultValue]);
|
|
3407
3463
|
if (textNode.length > 0) {
|
|
3408
3464
|
const menuMap = Dropdown.getMenuMap(config.menuItems, this.locale);
|
|
@@ -3525,7 +3581,7 @@ class Box {
|
|
|
3525
3581
|
if (typeof node === 'string') {
|
|
3526
3582
|
const component = boxes.get(node);
|
|
3527
3583
|
if (component === undefined) {
|
|
3528
|
-
throw new Error(`
|
|
3584
|
+
throw new Error(`The box "${node}" has not been defined yet.`);
|
|
3529
3585
|
}
|
|
3530
3586
|
const type = encode(component.type);
|
|
3531
3587
|
const name = encode(component.name);
|
|
@@ -3538,7 +3594,7 @@ class Box {
|
|
|
3538
3594
|
this.node = query(node);
|
|
3539
3595
|
const component = boxes.get(this.name);
|
|
3540
3596
|
if (component === undefined) {
|
|
3541
|
-
throw new Error(`
|
|
3597
|
+
throw new Error(`The box "${this.name}" has not been defined yet.`);
|
|
3542
3598
|
}
|
|
3543
3599
|
if (component.value && !this.node.hasAttr('value')) {
|
|
3544
3600
|
this.value = component.value;
|
|
@@ -3569,7 +3625,7 @@ class Box {
|
|
|
3569
3625
|
container.removeClass('lake-box-hovered');
|
|
3570
3626
|
});
|
|
3571
3627
|
container.on('click', () => {
|
|
3572
|
-
debug(`
|
|
3628
|
+
debug(`The box "${this.name}" (id = ${this.node.id}) value:`);
|
|
3573
3629
|
debug(this.value);
|
|
3574
3630
|
});
|
|
3575
3631
|
if (this.type === 'block' && this.node.isContentEditable) {
|
|
@@ -3590,11 +3646,11 @@ class Box {
|
|
|
3590
3646
|
if (value === '') {
|
|
3591
3647
|
return {};
|
|
3592
3648
|
}
|
|
3593
|
-
return JSON.parse(
|
|
3649
|
+
return JSON.parse(fromBase64(value));
|
|
3594
3650
|
}
|
|
3595
3651
|
// Sets the value of the box.
|
|
3596
3652
|
set value(value) {
|
|
3597
|
-
this.node.attr('value',
|
|
3653
|
+
this.node.attr('value', toBase64(JSON.stringify(value)));
|
|
3598
3654
|
}
|
|
3599
3655
|
updateValue(valueKey, valueValue) {
|
|
3600
3656
|
const value = this.value;
|
|
@@ -3608,7 +3664,7 @@ class Box {
|
|
|
3608
3664
|
}
|
|
3609
3665
|
this.value = value;
|
|
3610
3666
|
}
|
|
3611
|
-
// Returns the editor
|
|
3667
|
+
// Returns an instance of the editor that includes the box.
|
|
3612
3668
|
getEditor() {
|
|
3613
3669
|
const container = this.node.closest('div[contenteditable]');
|
|
3614
3670
|
return container.length > 0 ? editors.get(container.id) : undefined;
|
|
@@ -3654,15 +3710,16 @@ class Box {
|
|
|
3654
3710
|
if (component === undefined) {
|
|
3655
3711
|
return;
|
|
3656
3712
|
}
|
|
3713
|
+
this.event.emit('beforeunmount');
|
|
3714
|
+
this.event.removeAllListeners();
|
|
3657
3715
|
this.addFramework();
|
|
3658
3716
|
const content = component.render(this);
|
|
3659
3717
|
if (content !== undefined) {
|
|
3660
3718
|
const container = this.getContainer();
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
morph(container, newContainer);
|
|
3719
|
+
container.empty();
|
|
3720
|
+
container.append(content);
|
|
3664
3721
|
}
|
|
3665
|
-
debug(`
|
|
3722
|
+
debug(`The box "${this.name}" (id: ${this.node.id}) rendered`);
|
|
3666
3723
|
}
|
|
3667
3724
|
// Destroys a rendered box.
|
|
3668
3725
|
unmount() {
|
|
@@ -3670,7 +3727,7 @@ class Box {
|
|
|
3670
3727
|
this.event.emit('beforeunmount');
|
|
3671
3728
|
this.event.removeAllListeners();
|
|
3672
3729
|
this.node.empty();
|
|
3673
|
-
debug(`
|
|
3730
|
+
debug(`The box "${this.name}" (id: ${this.node.id}) unmounted`);
|
|
3674
3731
|
}
|
|
3675
3732
|
// Returns a HTML string of the box.
|
|
3676
3733
|
getHTML() {
|
|
@@ -3918,6 +3975,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3918
3975
|
encode: encode,
|
|
3919
3976
|
fileSize: fileSize,
|
|
3920
3977
|
fixNumberedList: fixNumberedList,
|
|
3978
|
+
fromBase64: fromBase64,
|
|
3921
3979
|
getBox: getBox,
|
|
3922
3980
|
getCSS: getCSS,
|
|
3923
3981
|
getDeepest: getDeepest,
|
|
@@ -3936,6 +3994,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3936
3994
|
setBlockIndent: setBlockIndent,
|
|
3937
3995
|
splitNodes: splitNodes,
|
|
3938
3996
|
template: template,
|
|
3997
|
+
toBase64: toBase64,
|
|
3939
3998
|
toHex: toHex,
|
|
3940
3999
|
toNodeList: toNodeList,
|
|
3941
4000
|
uploadFile: uploadFile,
|
|
@@ -4236,13 +4295,7 @@ class HTMLParser {
|
|
|
4236
4295
|
}
|
|
4237
4296
|
return html.trim();
|
|
4238
4297
|
}
|
|
4239
|
-
// Returns
|
|
4240
|
-
getNodeList() {
|
|
4241
|
-
const html = this.getHTML();
|
|
4242
|
-
const body = this.parseHTML(html);
|
|
4243
|
-
return body.children();
|
|
4244
|
-
}
|
|
4245
|
-
// Returns the result as document fragment.
|
|
4298
|
+
// Returns a document fragment.
|
|
4246
4299
|
getFragment() {
|
|
4247
4300
|
const html = this.getHTML();
|
|
4248
4301
|
const body = this.parseHTML(html);
|
|
@@ -5080,7 +5133,7 @@ function removeBox(range) {
|
|
|
5080
5133
|
return box;
|
|
5081
5134
|
}
|
|
5082
5135
|
|
|
5083
|
-
var version = "0.1.
|
|
5136
|
+
var version = "0.1.17";
|
|
5084
5137
|
|
|
5085
5138
|
// Returns the attributes of the element as an key-value object.
|
|
5086
5139
|
function getAttributes(node) {
|
|
@@ -5258,7 +5311,7 @@ class Selection {
|
|
|
5258
5311
|
insertBox(boxName, boxValue) {
|
|
5259
5312
|
const box = insertBox(this.range, boxName, boxValue);
|
|
5260
5313
|
if (!box) {
|
|
5261
|
-
throw new Error(`
|
|
5314
|
+
throw new Error(`The box "${boxName}" cannot be inserted outside the editor.`);
|
|
5262
5315
|
}
|
|
5263
5316
|
return box;
|
|
5264
5317
|
}
|
|
@@ -5361,7 +5414,7 @@ class History {
|
|
|
5361
5414
|
addIdToBoxes(node) {
|
|
5362
5415
|
node.find('lake-box').each(nativeNode => {
|
|
5363
5416
|
const boxNode = new Nodes(nativeNode);
|
|
5364
|
-
const id =
|
|
5417
|
+
const id = `${boxNode.attr('name')}-${boxNode.attr('value')}`;
|
|
5365
5418
|
boxNode.attr('id', id);
|
|
5366
5419
|
});
|
|
5367
5420
|
}
|
|
@@ -5381,7 +5434,8 @@ class History {
|
|
|
5381
5434
|
},
|
|
5382
5435
|
afterAttributeUpdated: (attributeName, nativeNode) => {
|
|
5383
5436
|
const node = new Nodes(nativeNode);
|
|
5384
|
-
if (
|
|
5437
|
+
if (['name', 'value'].indexOf(attributeName) >= 0 && node.name === 'lake-box') {
|
|
5438
|
+
getBox(node).unmount();
|
|
5385
5439
|
const instanceMap = getInstanceMap(container.id);
|
|
5386
5440
|
instanceMap.delete(node.id);
|
|
5387
5441
|
}
|
|
@@ -5397,7 +5451,7 @@ class History {
|
|
|
5397
5451
|
this.removeIdfromBoxes(otherContainer);
|
|
5398
5452
|
}
|
|
5399
5453
|
get canUndo() {
|
|
5400
|
-
return this.index > 1 && !!this.list[this.index -
|
|
5454
|
+
return this.index > 1 && !!this.list[this.index - 2];
|
|
5401
5455
|
}
|
|
5402
5456
|
get canRedo() {
|
|
5403
5457
|
return !!this.list[this.index];
|
|
@@ -5517,53 +5571,65 @@ class History {
|
|
|
5517
5571
|
}
|
|
5518
5572
|
}
|
|
5519
5573
|
|
|
5520
|
-
const
|
|
5521
|
-
['
|
|
5574
|
+
const aliasMap = new Map([
|
|
5575
|
+
['arrow-left', 'left'],
|
|
5576
|
+
['arrow-right', 'right'],
|
|
5577
|
+
['arrow-up', 'up'],
|
|
5578
|
+
['arrow-down', 'down'],
|
|
5522
5579
|
]);
|
|
5523
5580
|
class Keystroke {
|
|
5524
5581
|
constructor(container) {
|
|
5525
5582
|
this.keydownEventList = [];
|
|
5526
5583
|
this.keyupEventList = [];
|
|
5527
5584
|
this.container = container;
|
|
5585
|
+
this.container.on('keydown', event => {
|
|
5586
|
+
const keyboardEvent = event;
|
|
5587
|
+
for (const item of this.keydownEventList) {
|
|
5588
|
+
if (isKeyHotkey(item.type, keyboardEvent)) {
|
|
5589
|
+
if (item.listener(keyboardEvent) === false) {
|
|
5590
|
+
break;
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
});
|
|
5595
|
+
this.container.on('keyup', event => {
|
|
5596
|
+
const keyboardEvent = event;
|
|
5597
|
+
for (const item of this.keyupEventList) {
|
|
5598
|
+
if (isKeyHotkey(item.type, keyboardEvent)) {
|
|
5599
|
+
if (item.listener(keyboardEvent) === false) {
|
|
5600
|
+
break;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
});
|
|
5528
5605
|
}
|
|
5529
5606
|
normalizeType(type) {
|
|
5530
5607
|
var _a;
|
|
5531
|
-
type = (_a =
|
|
5532
|
-
type = type.replace(/(^|\+|\s)mod(\+|\s|$)/g, '$1$mod$2').
|
|
5533
|
-
replace(/shift|control|alt|meta|enter|tab|backspace|delete|space|escape|arrow-left|arrow-right|arrow-up|arrow-down/, (match) => match.charAt(0).toUpperCase() + camelCase(match.substring(1))).
|
|
5534
|
-
replace(/(^|\+|\s)([a-z])(\+|\s|$)/g, (match, p1, p2, p3) => `${p1}Key${p2.toUpperCase()}${p3}`);
|
|
5608
|
+
type = (_a = aliasMap.get(type)) !== null && _a !== void 0 ? _a : type;
|
|
5535
5609
|
return type;
|
|
5536
5610
|
}
|
|
5537
5611
|
// Sets a keydown shortcut.
|
|
5538
5612
|
setKeydown(type, listener) {
|
|
5539
5613
|
type = this.normalizeType(type);
|
|
5540
|
-
const handler = createKeybindingsHandler({
|
|
5541
|
-
[type]: event => listener(event),
|
|
5542
|
-
});
|
|
5543
5614
|
this.keydownEventList.push({
|
|
5544
5615
|
type,
|
|
5545
5616
|
listener,
|
|
5546
5617
|
});
|
|
5547
|
-
this.container.on('keydown', handler);
|
|
5548
5618
|
}
|
|
5549
5619
|
// Sets a keyup shortcut.
|
|
5550
5620
|
setKeyup(type, listener) {
|
|
5551
5621
|
type = this.normalizeType(type);
|
|
5552
|
-
const handler = createKeybindingsHandler({
|
|
5553
|
-
[type]: event => listener(event),
|
|
5554
|
-
});
|
|
5555
5622
|
this.keyupEventList.push({
|
|
5556
5623
|
type,
|
|
5557
5624
|
listener,
|
|
5558
5625
|
});
|
|
5559
|
-
this.container.on('keyup', handler);
|
|
5560
5626
|
}
|
|
5561
5627
|
// Executes the keydown shortcuts.
|
|
5562
5628
|
keydown(type) {
|
|
5563
5629
|
type = this.normalizeType(type);
|
|
5564
5630
|
for (const item of this.keydownEventList) {
|
|
5565
5631
|
if (item.type === type) {
|
|
5566
|
-
if (item.listener(new
|
|
5632
|
+
if (item.listener(new KeyboardEvent(type)) === false) {
|
|
5567
5633
|
break;
|
|
5568
5634
|
}
|
|
5569
5635
|
}
|
|
@@ -5574,7 +5640,7 @@ class Keystroke {
|
|
|
5574
5640
|
type = this.normalizeType(type);
|
|
5575
5641
|
for (const item of this.keyupEventList) {
|
|
5576
5642
|
if (item.type === type) {
|
|
5577
|
-
if (item.listener(new
|
|
5643
|
+
if (item.listener(new KeyboardEvent(type)) === false) {
|
|
5578
5644
|
break;
|
|
5579
5645
|
}
|
|
5580
5646
|
}
|
|
@@ -5730,9 +5796,7 @@ class Editor {
|
|
|
5730
5796
|
box.event.emit('blur');
|
|
5731
5797
|
});
|
|
5732
5798
|
}, 50, {
|
|
5733
|
-
|
|
5734
|
-
trailing: true,
|
|
5735
|
-
maxWait: 50,
|
|
5799
|
+
immediate: true,
|
|
5736
5800
|
});
|
|
5737
5801
|
// Triggers the statechange event when the current selection of the editor is changed.
|
|
5738
5802
|
this.emitStateChangeEvent = debounce(() => {
|
|
@@ -5776,10 +5840,8 @@ class Editor {
|
|
|
5776
5840
|
}
|
|
5777
5841
|
this.event.emit('statechange', state);
|
|
5778
5842
|
this.state = state;
|
|
5779
|
-
},
|
|
5780
|
-
|
|
5781
|
-
trailing: true,
|
|
5782
|
-
maxWait: 100,
|
|
5843
|
+
}, 50, {
|
|
5844
|
+
immediate: false,
|
|
5783
5845
|
});
|
|
5784
5846
|
if (!config.root) {
|
|
5785
5847
|
throw new Error('The root of the config must be specified.');
|
|
@@ -5814,7 +5876,7 @@ class Editor {
|
|
|
5814
5876
|
// Adds or Removes a placeholder class.
|
|
5815
5877
|
togglePlaceholderClass(value) {
|
|
5816
5878
|
value = denormalizeValue(value);
|
|
5817
|
-
const className = 'lake-
|
|
5879
|
+
const className = 'lake-placeholder';
|
|
5818
5880
|
if (value.replace('<focus />', '') === '<p><br /></p>') {
|
|
5819
5881
|
this.container.addClass(className);
|
|
5820
5882
|
}
|
|
@@ -5866,6 +5928,18 @@ class Editor {
|
|
|
5866
5928
|
this.container.on('compositionend', () => {
|
|
5867
5929
|
this.isComposing = false;
|
|
5868
5930
|
});
|
|
5931
|
+
this.container.on('beforeinput', event => {
|
|
5932
|
+
const inputEvent = event;
|
|
5933
|
+
// <p><br /><focus /></p>
|
|
5934
|
+
// When the caret is positioned behind a <br> tag, the input event is triggered twice after inserting a sharp(#) in composition mode.
|
|
5935
|
+
if (this.isComposing && inputEvent.inputType === 'insertText') {
|
|
5936
|
+
inputEvent.preventDefault();
|
|
5937
|
+
this.isComposing = false;
|
|
5938
|
+
this.history.save({
|
|
5939
|
+
inputType: 'insertText',
|
|
5940
|
+
});
|
|
5941
|
+
}
|
|
5942
|
+
});
|
|
5869
5943
|
this.container.on('input', event => {
|
|
5870
5944
|
const inputEvent = event;
|
|
5871
5945
|
// Here setTimeout is necessary because isComposing is not false after ending composition.
|
|
@@ -5877,6 +5951,9 @@ class Editor {
|
|
|
5877
5951
|
}
|
|
5878
5952
|
// isComposing is false after ending composition because compositionend event has been emitted.
|
|
5879
5953
|
if (this.isComposing) {
|
|
5954
|
+
if (inputEvent.inputType === 'insertCompositionText') {
|
|
5955
|
+
this.container.removeClass('lake-placeholder');
|
|
5956
|
+
}
|
|
5880
5957
|
this.event.emit('input', inputEvent);
|
|
5881
5958
|
return;
|
|
5882
5959
|
}
|
|
@@ -5959,6 +6036,7 @@ class Editor {
|
|
|
5959
6036
|
}
|
|
5960
6037
|
// Fixes wrong content, especially empty tag.
|
|
5961
6038
|
fixContent() {
|
|
6039
|
+
const range = this.selection.range;
|
|
5962
6040
|
let changed = false;
|
|
5963
6041
|
let children = this.container.children();
|
|
5964
6042
|
for (const child of children) {
|
|
@@ -5971,7 +6049,7 @@ class Editor {
|
|
|
5971
6049
|
children = this.container.children();
|
|
5972
6050
|
if (children.length === 0) {
|
|
5973
6051
|
this.container.html('<p><br /></p>');
|
|
5974
|
-
|
|
6052
|
+
range.shrinkAfter(this.container);
|
|
5975
6053
|
changed = true;
|
|
5976
6054
|
debug('Content fixed: default paragraph was added');
|
|
5977
6055
|
}
|
|
@@ -5981,11 +6059,23 @@ class Editor {
|
|
|
5981
6059
|
const paragraph = query('<p />');
|
|
5982
6060
|
child.before(paragraph);
|
|
5983
6061
|
paragraph.append(child);
|
|
5984
|
-
|
|
6062
|
+
range.shrinkAfter(paragraph);
|
|
5985
6063
|
changed = true;
|
|
5986
6064
|
debug(`Content fixed: void element "${child.name}" was wrapped in paragraph`);
|
|
5987
6065
|
}
|
|
5988
6066
|
}
|
|
6067
|
+
// In composition mode (e.g., when a user starts entering a Chinese character using a Pinyin IME),
|
|
6068
|
+
// uncompleted text is inserted if the caret is positioned behind a <br> tag.
|
|
6069
|
+
// To fix this bug, the caret needs to be moved to the front of the <br> tag.
|
|
6070
|
+
if (range.isCollapsed) {
|
|
6071
|
+
const prevNode = range.getPrevNode();
|
|
6072
|
+
const nextNode = range.getNextNode();
|
|
6073
|
+
if (prevNode.name === 'br' && nextNode.length === 0) {
|
|
6074
|
+
range.setStartBefore(prevNode);
|
|
6075
|
+
range.collapseToStart();
|
|
6076
|
+
debug('Range fixed: the caret has been moved to the front of the <br> tag');
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
5989
6079
|
return changed;
|
|
5990
6080
|
}
|
|
5991
6081
|
// Sets default config for a plugin.
|
|
@@ -6033,8 +6123,12 @@ class Editor {
|
|
|
6033
6123
|
}
|
|
6034
6124
|
// Scrolls to the caret or the range of the selection.
|
|
6035
6125
|
scrollToCaret() {
|
|
6126
|
+
const range = this.selection.range;
|
|
6127
|
+
if (range.isBox) {
|
|
6128
|
+
return;
|
|
6129
|
+
}
|
|
6036
6130
|
// Creates an artificial caret that is the same size as the caret at the current caret position.
|
|
6037
|
-
const rangeRect =
|
|
6131
|
+
const rangeRect = range.getRect();
|
|
6038
6132
|
if (rangeRect.x === 0 || rangeRect.y === 0) {
|
|
6039
6133
|
return;
|
|
6040
6134
|
}
|
|
@@ -6623,6 +6717,15 @@ const toolbarItems = [
|
|
|
6623
6717
|
editor.command.execute(value);
|
|
6624
6718
|
},
|
|
6625
6719
|
},
|
|
6720
|
+
{
|
|
6721
|
+
name: 'equation',
|
|
6722
|
+
type: 'button',
|
|
6723
|
+
icon: icons.get('equation'),
|
|
6724
|
+
tooltip: locale => locale.toolbar.equation(),
|
|
6725
|
+
onClick: (editor, value) => {
|
|
6726
|
+
editor.command.execute(value);
|
|
6727
|
+
},
|
|
6728
|
+
},
|
|
6626
6729
|
{
|
|
6627
6730
|
name: 'heading',
|
|
6628
6731
|
type: 'dropdown',
|
|
@@ -6838,6 +6941,7 @@ class Toolbar {
|
|
|
6838
6941
|
menuType: item.menuType,
|
|
6839
6942
|
menuItems: item.menuItems,
|
|
6840
6943
|
menuWidth: item.menuWidth,
|
|
6944
|
+
menuHeight: item.menuHeight,
|
|
6841
6945
|
tabIndex: -1,
|
|
6842
6946
|
placement: this.placement === 'top' ? 'bottom' : 'top',
|
|
6843
6947
|
onSelect: value => {
|
|
@@ -6991,8 +7095,10 @@ const hrBox = {
|
|
|
6991
7095
|
if (!editor) {
|
|
6992
7096
|
return;
|
|
6993
7097
|
}
|
|
7098
|
+
const boxContainer = box.getContainer();
|
|
6994
7099
|
const rootNode = query('<div class="lake-hr"><hr /></div>');
|
|
6995
|
-
|
|
7100
|
+
boxContainer.empty();
|
|
7101
|
+
boxContainer.append(rootNode);
|
|
6996
7102
|
rootNode.on('click', () => {
|
|
6997
7103
|
editor.selection.selectBox(box);
|
|
6998
7104
|
});
|
|
@@ -7087,10 +7193,10 @@ const codeBlockBox = {
|
|
|
7087
7193
|
return;
|
|
7088
7194
|
}
|
|
7089
7195
|
const rootNode = query('<div class="lake-code-block" />');
|
|
7090
|
-
const
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7196
|
+
const boxContainer = box.getContainer();
|
|
7197
|
+
boxContainer.css('width', `${editor.container.innerWidth() - 2}px`);
|
|
7198
|
+
boxContainer.empty();
|
|
7199
|
+
boxContainer.append(rootNode);
|
|
7094
7200
|
const codeBlockNativeNode = rootNode.get(0);
|
|
7095
7201
|
if (!codeBlockNativeNode) {
|
|
7096
7202
|
return;
|
|
@@ -7165,24 +7271,24 @@ const codeBlockBox = {
|
|
|
7165
7271
|
defaultValue: langItem ? boxValue.lang : codeBlockConfig.defaultLang,
|
|
7166
7272
|
tooltip: editor.locale.codeBlock.langType(),
|
|
7167
7273
|
menuType: 'list',
|
|
7274
|
+
menuHeight: '200px',
|
|
7168
7275
|
menuItems: langItems.map((item) => ({
|
|
7169
7276
|
value: item.value,
|
|
7170
7277
|
text: item.text,
|
|
7171
7278
|
})),
|
|
7172
7279
|
onSelect: value => {
|
|
7173
|
-
const item = langItemMap.get(value);
|
|
7174
|
-
codeEditor.dispatch({
|
|
7175
|
-
effects: language.reconfigure(item && item.component ? item.component() : []),
|
|
7176
|
-
});
|
|
7177
7280
|
box.updateValue({
|
|
7178
7281
|
lang: value,
|
|
7179
7282
|
});
|
|
7283
|
+
box.unmount();
|
|
7284
|
+
box.render();
|
|
7285
|
+
editor.selection.selectBox(box);
|
|
7180
7286
|
editor.history.save();
|
|
7181
7287
|
},
|
|
7182
7288
|
});
|
|
7183
7289
|
dropdown.render();
|
|
7184
7290
|
const resizeListener = () => {
|
|
7185
|
-
|
|
7291
|
+
boxContainer.css('width', `${editor.container.innerWidth() - 2}px`);
|
|
7186
7292
|
};
|
|
7187
7293
|
editor.event.on('resize', resizeListener);
|
|
7188
7294
|
rootNode.on('click', () => {
|
|
@@ -7448,7 +7554,7 @@ function openFullScreen(box) {
|
|
|
7448
7554
|
lightbox.loadAndOpen(currentIndex);
|
|
7449
7555
|
}
|
|
7450
7556
|
// Displays error icon and filename.
|
|
7451
|
-
function renderError(rootNode, box) {
|
|
7557
|
+
function renderError$1(rootNode, box) {
|
|
7452
7558
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7453
7559
|
const editor = box.getEditor();
|
|
7454
7560
|
if (!editor) {
|
|
@@ -7493,7 +7599,7 @@ function renderUploading(rootNode, box) {
|
|
|
7493
7599
|
const value = box.value;
|
|
7494
7600
|
const imageInfo = yield getImageInfo(value.url);
|
|
7495
7601
|
if (!imageInfo.width || !imageInfo.height) {
|
|
7496
|
-
yield renderError(rootNode, box);
|
|
7602
|
+
yield renderError$1(rootNode, box);
|
|
7497
7603
|
return;
|
|
7498
7604
|
}
|
|
7499
7605
|
const maxWidth = editor.container.innerWidth() - 2;
|
|
@@ -7557,7 +7663,7 @@ function renderDone(rootNode, box) {
|
|
|
7557
7663
|
return;
|
|
7558
7664
|
}
|
|
7559
7665
|
if (!imageInfo.width || !imageInfo.height) {
|
|
7560
|
-
yield renderError(rootNode, box);
|
|
7666
|
+
yield renderError$1(rootNode, box);
|
|
7561
7667
|
return;
|
|
7562
7668
|
}
|
|
7563
7669
|
let width = value.width;
|
|
@@ -7630,26 +7736,26 @@ const imageBox = {
|
|
|
7630
7736
|
box.node.hide();
|
|
7631
7737
|
return;
|
|
7632
7738
|
}
|
|
7633
|
-
const
|
|
7634
|
-
if (value.width && value.height &&
|
|
7635
|
-
|
|
7739
|
+
const boxContainer = box.getContainer();
|
|
7740
|
+
if (value.width && value.height && boxContainer.find('.lake-progress').length === 0) {
|
|
7741
|
+
boxContainer.css({
|
|
7636
7742
|
width: `${value.width}px`,
|
|
7637
7743
|
height: `${value.height}px`,
|
|
7638
7744
|
});
|
|
7639
|
-
|
|
7745
|
+
boxContainer.empty();
|
|
7640
7746
|
const placeholderNode = query('<div class="lake-image-placeholder" />');
|
|
7641
|
-
|
|
7747
|
+
boxContainer.append(placeholderNode);
|
|
7642
7748
|
const imageIcon = icons.get('image');
|
|
7643
7749
|
if (imageIcon) {
|
|
7644
7750
|
placeholderNode.append(imageIcon);
|
|
7645
7751
|
}
|
|
7646
7752
|
}
|
|
7647
|
-
if (
|
|
7753
|
+
if (boxContainer.first().length === 0) {
|
|
7648
7754
|
// The following code is for unit testing because some test cases need to
|
|
7649
7755
|
// select the content of the box before it is completely loaded.
|
|
7650
7756
|
// Example:
|
|
7651
7757
|
// range.setStart(box.getContainer(), 1);
|
|
7652
|
-
|
|
7758
|
+
boxContainer.append('<div />');
|
|
7653
7759
|
}
|
|
7654
7760
|
// for test
|
|
7655
7761
|
if (value.status === 'loading') {
|
|
@@ -7662,14 +7768,14 @@ const imageBox = {
|
|
|
7662
7768
|
promise = renderUploading(rootNode, box);
|
|
7663
7769
|
}
|
|
7664
7770
|
else if (value.status === 'error') {
|
|
7665
|
-
promise = renderError(rootNode, box);
|
|
7771
|
+
promise = renderError$1(rootNode, box);
|
|
7666
7772
|
}
|
|
7667
7773
|
else {
|
|
7668
7774
|
promise = renderDone(rootNode, box);
|
|
7669
7775
|
}
|
|
7670
7776
|
promise.then(() => {
|
|
7671
|
-
|
|
7672
|
-
|
|
7777
|
+
boxContainer.empty();
|
|
7778
|
+
boxContainer.append(rootNode);
|
|
7673
7779
|
rootNode.find('.lake-button-view').on('click', () => openFullScreen(box));
|
|
7674
7780
|
if (editor.readonly) {
|
|
7675
7781
|
rootNode.find('.lake-button-remove').hide();
|
|
@@ -7829,12 +7935,12 @@ const videoBox = {
|
|
|
7829
7935
|
showVideo(box);
|
|
7830
7936
|
},
|
|
7831
7937
|
});
|
|
7832
|
-
formNode.find('input[name="url"]').on('keydown',
|
|
7833
|
-
'
|
|
7938
|
+
formNode.find('input[name="url"]').on('keydown', event => {
|
|
7939
|
+
if (isKeyHotkey('enter', event)) {
|
|
7834
7940
|
event.preventDefault();
|
|
7835
7941
|
button.node.emit('click');
|
|
7836
|
-
}
|
|
7837
|
-
})
|
|
7942
|
+
}
|
|
7943
|
+
});
|
|
7838
7944
|
button.render();
|
|
7839
7945
|
rootNode.append(formNode);
|
|
7840
7946
|
appendButtonGroup(box);
|
|
@@ -7919,12 +8025,12 @@ const fileBox = {
|
|
|
7919
8025
|
box.node.hide();
|
|
7920
8026
|
return;
|
|
7921
8027
|
}
|
|
7922
|
-
const
|
|
8028
|
+
const boxContainer = box.getContainer();
|
|
7923
8029
|
const rootNode = query('<div class="lake-file" />');
|
|
7924
8030
|
rootNode.addClass(`lake-file-${value.status}`);
|
|
7925
8031
|
appendContent(rootNode, box);
|
|
7926
|
-
|
|
7927
|
-
|
|
8032
|
+
boxContainer.empty();
|
|
8033
|
+
boxContainer.append(rootNode);
|
|
7928
8034
|
if (!editor.readonly) {
|
|
7929
8035
|
rootNode.on('click', () => {
|
|
7930
8036
|
editor.selection.selectBox(box);
|
|
@@ -7949,16 +8055,100 @@ const emojiBox = {
|
|
|
7949
8055
|
return;
|
|
7950
8056
|
}
|
|
7951
8057
|
const value = box.value;
|
|
8058
|
+
const boxContainer = box.getContainer();
|
|
7952
8059
|
const rootNode = query(safeTemplate `
|
|
7953
8060
|
<div class="lake-emoji"><img src="${value.url}" title="${value.title}" /></div>
|
|
7954
8061
|
`);
|
|
7955
|
-
|
|
8062
|
+
boxContainer.empty();
|
|
8063
|
+
boxContainer.append(rootNode);
|
|
7956
8064
|
rootNode.on('click', () => {
|
|
7957
8065
|
editor.selection.selectBox(box);
|
|
7958
8066
|
});
|
|
7959
8067
|
},
|
|
7960
8068
|
};
|
|
7961
8069
|
|
|
8070
|
+
const defaultExpression = String.raw `\sqrt{x}`;
|
|
8071
|
+
function renderError(box) {
|
|
8072
|
+
const editor = box.getEditor();
|
|
8073
|
+
if (!editor) {
|
|
8074
|
+
return;
|
|
8075
|
+
}
|
|
8076
|
+
if (editor.readonly) {
|
|
8077
|
+
box.node.hide();
|
|
8078
|
+
return;
|
|
8079
|
+
}
|
|
8080
|
+
const defaultCode = (box.value.code || '').trim();
|
|
8081
|
+
const rootNode = box.getContainer().find('.lake-equation');
|
|
8082
|
+
rootNode.addClass('lake-equation-error');
|
|
8083
|
+
rootNode.text(defaultCode);
|
|
8084
|
+
rootNode.on('click', () => {
|
|
8085
|
+
editor.selection.selectBox(box);
|
|
8086
|
+
});
|
|
8087
|
+
editor.config.onMessage('warning', `
|
|
8088
|
+
The box "${box.name}" (id: ${box.node.id}) failed to display because window.katex was not found.
|
|
8089
|
+
Please check if the "katex" library is added to this page.
|
|
8090
|
+
`.trim());
|
|
8091
|
+
}
|
|
8092
|
+
const equationBox = {
|
|
8093
|
+
type: 'inline',
|
|
8094
|
+
name: 'equation',
|
|
8095
|
+
render: box => {
|
|
8096
|
+
const editor = box.getEditor();
|
|
8097
|
+
if (!editor) {
|
|
8098
|
+
return;
|
|
8099
|
+
}
|
|
8100
|
+
const rootNode = query('<div class="lake-equation" />');
|
|
8101
|
+
const boxContainer = box.getContainer();
|
|
8102
|
+
boxContainer.empty();
|
|
8103
|
+
boxContainer.append(rootNode);
|
|
8104
|
+
const katex = window.katex;
|
|
8105
|
+
if (!katex) {
|
|
8106
|
+
renderError(box);
|
|
8107
|
+
return;
|
|
8108
|
+
}
|
|
8109
|
+
const defaultCode = (box.value.code || '').trim();
|
|
8110
|
+
const viewNode = query('<div class="lake-equation-view" />');
|
|
8111
|
+
rootNode.append(viewNode);
|
|
8112
|
+
viewNode.html(window.katex.renderToString(defaultCode || defaultExpression, {
|
|
8113
|
+
throwOnError: false,
|
|
8114
|
+
}));
|
|
8115
|
+
viewNode.on('click', () => {
|
|
8116
|
+
editor.selection.selectBox(box);
|
|
8117
|
+
});
|
|
8118
|
+
const formNode = query(safeTemplate `
|
|
8119
|
+
<div class="lake-equation-form">
|
|
8120
|
+
<div class="lake-row">
|
|
8121
|
+
<textarea name="code" placeholder="${editor.locale.equation.placeholder()}"></textarea>
|
|
8122
|
+
</div>
|
|
8123
|
+
<div class="lake-row lake-button-row"></div>
|
|
8124
|
+
</div>
|
|
8125
|
+
`);
|
|
8126
|
+
rootNode.append(formNode);
|
|
8127
|
+
const textareaNode = formNode.find('textarea');
|
|
8128
|
+
const textareaNativeNode = textareaNode.get(0);
|
|
8129
|
+
textareaNativeNode.value = defaultCode;
|
|
8130
|
+
textareaNode.on('input', () => {
|
|
8131
|
+
const code = textareaNativeNode.value.trim();
|
|
8132
|
+
viewNode.html(window.katex.renderToString(code || defaultExpression, {
|
|
8133
|
+
throwOnError: false,
|
|
8134
|
+
}));
|
|
8135
|
+
box.updateValue('code', code);
|
|
8136
|
+
});
|
|
8137
|
+
const button = new Button({
|
|
8138
|
+
root: formNode.find('.lake-button-row'),
|
|
8139
|
+
name: 'save',
|
|
8140
|
+
type: 'primary',
|
|
8141
|
+
text: editor.locale.equation.save(),
|
|
8142
|
+
onClick: () => {
|
|
8143
|
+
editor.selection.range.selectBoxEnd(box.node);
|
|
8144
|
+
editor.selection.sync();
|
|
8145
|
+
editor.history.save();
|
|
8146
|
+
},
|
|
8147
|
+
});
|
|
8148
|
+
button.render();
|
|
8149
|
+
},
|
|
8150
|
+
};
|
|
8151
|
+
|
|
7962
8152
|
var copy = (editor) => {
|
|
7963
8153
|
editor.event.on('copy', event => {
|
|
7964
8154
|
const range = editor.selection.range;
|
|
@@ -8393,6 +8583,7 @@ var undo = (editor) => {
|
|
|
8393
8583
|
return;
|
|
8394
8584
|
}
|
|
8395
8585
|
editor.command.add('undo', {
|
|
8586
|
+
isDisabled: () => !editor.history.canUndo,
|
|
8396
8587
|
execute: () => {
|
|
8397
8588
|
editor.history.undo();
|
|
8398
8589
|
},
|
|
@@ -8412,6 +8603,7 @@ var redo = (editor) => {
|
|
|
8412
8603
|
return;
|
|
8413
8604
|
}
|
|
8414
8605
|
editor.command.add('redo', {
|
|
8606
|
+
isDisabled: () => !editor.history.canRedo,
|
|
8415
8607
|
execute: () => {
|
|
8416
8608
|
editor.history.redo();
|
|
8417
8609
|
},
|
|
@@ -9052,18 +9244,18 @@ class LinkPopup {
|
|
|
9052
9244
|
},
|
|
9053
9245
|
});
|
|
9054
9246
|
button.render();
|
|
9055
|
-
this.container.find('input[name="url"]').on('keydown',
|
|
9056
|
-
'
|
|
9247
|
+
this.container.find('input[name="url"]').on('keydown', event => {
|
|
9248
|
+
if (isKeyHotkey('enter', event)) {
|
|
9057
9249
|
event.preventDefault();
|
|
9058
9250
|
button.node.emit('click');
|
|
9059
|
-
}
|
|
9060
|
-
})
|
|
9061
|
-
this.container.find('input[name="title"]').on('keydown',
|
|
9062
|
-
'
|
|
9251
|
+
}
|
|
9252
|
+
});
|
|
9253
|
+
this.container.find('input[name="title"]').on('keydown', event => {
|
|
9254
|
+
if (isKeyHotkey('enter', event)) {
|
|
9063
9255
|
event.preventDefault();
|
|
9064
9256
|
button.node.emit('click');
|
|
9065
|
-
}
|
|
9066
|
-
})
|
|
9257
|
+
}
|
|
9258
|
+
});
|
|
9067
9259
|
}
|
|
9068
9260
|
// Remove link
|
|
9069
9261
|
appendUnlinkButton() {
|
|
@@ -9386,6 +9578,36 @@ var emoji = (editor) => {
|
|
|
9386
9578
|
});
|
|
9387
9579
|
};
|
|
9388
9580
|
|
|
9581
|
+
var equation = (editor) => {
|
|
9582
|
+
if (!window.katex) {
|
|
9583
|
+
return;
|
|
9584
|
+
}
|
|
9585
|
+
if (editor.readonly) {
|
|
9586
|
+
return;
|
|
9587
|
+
}
|
|
9588
|
+
editor.command.add('equation', {
|
|
9589
|
+
execute: (value) => {
|
|
9590
|
+
const box = editor.selection.insertBox('equation', value);
|
|
9591
|
+
editor.selection.selectBox(box);
|
|
9592
|
+
editor.history.save();
|
|
9593
|
+
},
|
|
9594
|
+
});
|
|
9595
|
+
};
|
|
9596
|
+
|
|
9597
|
+
var specialCharacter = (editor) => {
|
|
9598
|
+
if (editor.readonly) {
|
|
9599
|
+
return;
|
|
9600
|
+
}
|
|
9601
|
+
editor.command.add('specialCharacter', {
|
|
9602
|
+
execute: (value) => {
|
|
9603
|
+
const fragment = new Fragment();
|
|
9604
|
+
fragment.append(document.createTextNode(value));
|
|
9605
|
+
editor.selection.insertFragment(fragment);
|
|
9606
|
+
editor.history.save();
|
|
9607
|
+
},
|
|
9608
|
+
});
|
|
9609
|
+
};
|
|
9610
|
+
|
|
9389
9611
|
const headingTypeMap = new Map([
|
|
9390
9612
|
['#', 'h1'],
|
|
9391
9613
|
['##', 'h2'],
|
|
@@ -10008,7 +10230,8 @@ var backspaceKey = (editor) => {
|
|
|
10008
10230
|
editor.history.save();
|
|
10009
10231
|
return;
|
|
10010
10232
|
}
|
|
10011
|
-
|
|
10233
|
+
const nextNode = range.getNextNode();
|
|
10234
|
+
if (prevNode.name === 'br' && nextNode.length > 0) {
|
|
10012
10235
|
event.preventDefault();
|
|
10013
10236
|
range.setStartBefore(prevNode);
|
|
10014
10237
|
range.collapseToStart();
|
|
@@ -10156,8 +10379,12 @@ var tabKey = (editor) => {
|
|
|
10156
10379
|
if (editor.config.indentWithTab === false) {
|
|
10157
10380
|
return;
|
|
10158
10381
|
}
|
|
10382
|
+
const range = editor.selection.range;
|
|
10383
|
+
if (range.isInsideBox) {
|
|
10384
|
+
return;
|
|
10385
|
+
}
|
|
10159
10386
|
event.preventDefault();
|
|
10160
|
-
const blocks =
|
|
10387
|
+
const blocks = range.getBlocks();
|
|
10161
10388
|
for (const block of blocks) {
|
|
10162
10389
|
if (block.name !== 'p' || block.css('text-indent') === '2em') {
|
|
10163
10390
|
setBlockIndent(block, 'increase');
|
|
@@ -10321,6 +10548,7 @@ Editor.box.add(imageBox);
|
|
|
10321
10548
|
Editor.box.add(videoBox);
|
|
10322
10549
|
Editor.box.add(fileBox);
|
|
10323
10550
|
Editor.box.add(emojiBox);
|
|
10551
|
+
Editor.box.add(equationBox);
|
|
10324
10552
|
Editor.plugin.add(copy);
|
|
10325
10553
|
Editor.plugin.add(cut);
|
|
10326
10554
|
Editor.plugin.add(paste);
|
|
@@ -10353,6 +10581,8 @@ Editor.plugin.add(image);
|
|
|
10353
10581
|
Editor.plugin.add(video);
|
|
10354
10582
|
Editor.plugin.add(file);
|
|
10355
10583
|
Editor.plugin.add(emoji);
|
|
10584
|
+
Editor.plugin.add(equation);
|
|
10585
|
+
Editor.plugin.add(specialCharacter);
|
|
10356
10586
|
Editor.plugin.add(markdown);
|
|
10357
10587
|
Editor.plugin.add(enterKey);
|
|
10358
10588
|
Editor.plugin.add(shiftEnterKey);
|