lakelib 0.3.8 → 0.3.10
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/dist/lake.min.css +6 -6
- package/dist/lake.min.js +36 -41
- package/dist/lake.min.js.map +1 -1
- package/lib/lake.css +116 -77
- package/lib/lake.d.ts +111 -2
- package/lib/lake.js +493 -295
- package/lib/lake.js.map +1 -1
- package/package.json +7 -7
package/lib/lake.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import EventEmitter from 'eventemitter3';
|
|
2
2
|
import { i18nObject as i18nObject$1 } from 'typesafe-i18n';
|
|
3
|
+
import { isKeyHotkey } from 'is-hotkey';
|
|
3
4
|
import debounce from 'debounce';
|
|
4
5
|
import isEqual from 'fast-deep-equal/es6';
|
|
5
|
-
import { isKeyHotkey } from 'is-hotkey';
|
|
6
6
|
import 'photoswipe/style.css';
|
|
7
7
|
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
|
8
8
|
import PhotoSwipe from 'photoswipe';
|
|
@@ -149,6 +149,8 @@ var specialCharacter$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\"
|
|
|
149
149
|
|
|
150
150
|
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>";
|
|
151
151
|
|
|
152
|
+
var twitter$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M214.75,211.71l-62.6-98.38,61.77-67.95a8,8,0,0,0-11.84-10.76L143.24,99.34,102.75,35.71A8,8,0,0,0,96,32H48a8,8,0,0,0-6.75,12.3l62.6,98.37-61.77,68a8,8,0,1,0,11.84,10.76l58.84-64.72,40.49,63.63A8,8,0,0,0,160,224h48a8,8,0,0,0,6.75-12.29ZM164.39,208,62.57,48h29L193.43,208Z\"></path></svg>";
|
|
153
|
+
|
|
152
154
|
var table$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M216,48H40A16,16,0,0,0,24,64V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V64A16,16,0,0,0,216,48ZM104,144V112h48v32Zm48,16v32H104V160ZM40,112H88v32H40Zm64-16V64h48V96Zm64,16h48v32H168Zm48-16H168V64h48ZM88,64V96H40V64ZM40,160H88v32H40Zm176,32H168V160h48v32Z\"></path></svg>";
|
|
153
155
|
|
|
154
156
|
var unlink = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M190.63,65.37a32,32,0,0,0-45.19-.06L133.79,77.52a8,8,0,0,1-11.58-11l11.72-12.29a1.59,1.59,0,0,1,.13-.13,48,48,0,0,1,67.88,67.88,1.59,1.59,0,0,1-.13.13l-12.29,11.72a8,8,0,0,1-11-11.58l12.21-11.65A32,32,0,0,0,190.63,65.37ZM122.21,178.48l-11.65,12.21a32,32,0,0,1-45.25-45.25l12.21-11.65a8,8,0,0,0-11-11.58L54.19,133.93a1.59,1.59,0,0,0-.13.13,48,48,0,0,0,67.88,67.88,1.59,1.59,0,0,0,.13-.13l11.72-12.29a8,8,0,1,0-11.58-11ZM208,152H184a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16ZM48,104H72a8,8,0,0,0,0-16H48a8,8,0,0,0,0,16Zm112,72a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V184A8,8,0,0,0,160,176ZM96,80a8,8,0,0,0,8-8V48a8,8,0,0,0-16,0V72A8,8,0,0,0,96,80Z\"></path></svg>";
|
|
@@ -263,6 +265,7 @@ const icons = new Map([
|
|
|
263
265
|
['emoji', emoji$1],
|
|
264
266
|
['specialCharacter', specialCharacter$1],
|
|
265
267
|
['equation', equation$1],
|
|
268
|
+
['twitter', twitter$1],
|
|
266
269
|
['table', table$1],
|
|
267
270
|
// link
|
|
268
271
|
['unlink', unlink],
|
|
@@ -1649,7 +1652,7 @@ class Range {
|
|
|
1649
1652
|
return this.range.endOffset;
|
|
1650
1653
|
}
|
|
1651
1654
|
/**
|
|
1652
|
-
* The deepest
|
|
1655
|
+
* The deepest node, or the lowest point in the document tree, that contains both boundary points of the range.
|
|
1653
1656
|
*/
|
|
1654
1657
|
get commonAncestor() {
|
|
1655
1658
|
return new Nodes(this.range.commonAncestorContainer);
|
|
@@ -2864,6 +2867,15 @@ const toolbarItems = [
|
|
|
2864
2867
|
editor.command.execute(value);
|
|
2865
2868
|
},
|
|
2866
2869
|
},
|
|
2870
|
+
{
|
|
2871
|
+
name: 'twitter',
|
|
2872
|
+
type: 'button',
|
|
2873
|
+
icon: icons.get('twitter'),
|
|
2874
|
+
tooltip: locale => locale.toolbar.twitter(),
|
|
2875
|
+
onClick: (editor, value) => {
|
|
2876
|
+
editor.command.execute(value);
|
|
2877
|
+
},
|
|
2878
|
+
},
|
|
2867
2879
|
{
|
|
2868
2880
|
name: 'heading',
|
|
2869
2881
|
type: 'dropdown',
|
|
@@ -3318,7 +3330,7 @@ var enUS = {
|
|
|
3318
3330
|
formatPainter: 'Format painter',
|
|
3319
3331
|
link: 'Link',
|
|
3320
3332
|
hr: 'Horizontal line',
|
|
3321
|
-
video: '
|
|
3333
|
+
video: 'YouTube',
|
|
3322
3334
|
codeBlock: 'Code block',
|
|
3323
3335
|
heading: 'Heading',
|
|
3324
3336
|
heading1: 'Heading 1',
|
|
@@ -3340,6 +3352,7 @@ var enUS = {
|
|
|
3340
3352
|
file: 'File',
|
|
3341
3353
|
emoji: 'Emoji',
|
|
3342
3354
|
equation: 'Mathematical formula',
|
|
3355
|
+
twitter: 'X (Tweet)',
|
|
3343
3356
|
removeColor: 'Remove color',
|
|
3344
3357
|
},
|
|
3345
3358
|
slash: {
|
|
@@ -3379,10 +3392,12 @@ var enUS = {
|
|
|
3379
3392
|
hrDesc: 'Insert a horizontal line',
|
|
3380
3393
|
codeBlock: 'Code block',
|
|
3381
3394
|
codeBlockDesc: 'Insert a code block',
|
|
3382
|
-
video: '
|
|
3383
|
-
videoDesc: 'Insert a video
|
|
3395
|
+
video: 'YouTube',
|
|
3396
|
+
videoDesc: 'Insert a YouTube video',
|
|
3384
3397
|
equation: 'Mathematical formula',
|
|
3385
3398
|
equationDesc: 'Insert a TeX expression',
|
|
3399
|
+
twitter: 'Tweet',
|
|
3400
|
+
twitterDesc: 'Insert an X (Tweet)',
|
|
3386
3401
|
image: 'Image',
|
|
3387
3402
|
imageDesc: 'Upload an image',
|
|
3388
3403
|
file: 'File',
|
|
@@ -3446,7 +3461,7 @@ var enUS = {
|
|
|
3446
3461
|
video: {
|
|
3447
3462
|
embed: 'Embed video',
|
|
3448
3463
|
remove: 'Delete',
|
|
3449
|
-
description: 'Paste a link to embed
|
|
3464
|
+
description: 'Paste a YouTube link to embed the video.',
|
|
3450
3465
|
url: 'Link',
|
|
3451
3466
|
urlError: 'Please enter a valid link.',
|
|
3452
3467
|
},
|
|
@@ -3458,6 +3473,13 @@ var enUS = {
|
|
|
3458
3473
|
help: 'Supported functions',
|
|
3459
3474
|
placeholder: 'Type a TeX expression...',
|
|
3460
3475
|
},
|
|
3476
|
+
twitter: {
|
|
3477
|
+
embed: 'Embed Tweet',
|
|
3478
|
+
remove: 'Delete',
|
|
3479
|
+
description: 'Paste an X (Twitter) link to embed the post.',
|
|
3480
|
+
url: 'Link',
|
|
3481
|
+
urlError: 'Please enter a valid link.',
|
|
3482
|
+
},
|
|
3461
3483
|
};
|
|
3462
3484
|
|
|
3463
3485
|
var zhCN = {
|
|
@@ -3487,7 +3509,7 @@ var zhCN = {
|
|
|
3487
3509
|
formatPainter: '格式刷',
|
|
3488
3510
|
link: '链接',
|
|
3489
3511
|
hr: '分割线',
|
|
3490
|
-
video: '
|
|
3512
|
+
video: 'YouTube',
|
|
3491
3513
|
codeBlock: '代码块',
|
|
3492
3514
|
heading: '标题',
|
|
3493
3515
|
heading1: '标题 1',
|
|
@@ -3509,6 +3531,7 @@ var zhCN = {
|
|
|
3509
3531
|
file: '文件',
|
|
3510
3532
|
emoji: '表情',
|
|
3511
3533
|
equation: '数学公式',
|
|
3534
|
+
twitter: 'X (Tweet)',
|
|
3512
3535
|
removeColor: '默认',
|
|
3513
3536
|
},
|
|
3514
3537
|
slash: {
|
|
@@ -3548,10 +3571,12 @@ var zhCN = {
|
|
|
3548
3571
|
hrDesc: '插入分割线',
|
|
3549
3572
|
codeBlock: '代码块',
|
|
3550
3573
|
codeBlockDesc: '插入代码块',
|
|
3551
|
-
video: '
|
|
3574
|
+
video: 'YouTube',
|
|
3552
3575
|
videoDesc: '插入 YouTube 视频',
|
|
3553
3576
|
equation: '数学公式',
|
|
3554
3577
|
equationDesc: '支持 TeX 语法',
|
|
3578
|
+
twitter: 'Tweet',
|
|
3579
|
+
twitterDesc: '插入 Tweet',
|
|
3555
3580
|
image: '图片',
|
|
3556
3581
|
imageDesc: '上传图片',
|
|
3557
3582
|
file: '文件',
|
|
@@ -3627,6 +3652,13 @@ var zhCN = {
|
|
|
3627
3652
|
help: '支持的功能',
|
|
3628
3653
|
placeholder: '请输入 TeX 表达式',
|
|
3629
3654
|
},
|
|
3655
|
+
twitter: {
|
|
3656
|
+
embed: '嵌入 Tweet',
|
|
3657
|
+
remove: '删除',
|
|
3658
|
+
description: '在下面的输入框里,粘贴 X (Twitter) 链接。',
|
|
3659
|
+
url: '链接',
|
|
3660
|
+
urlError: '请输入有效的链接。',
|
|
3661
|
+
},
|
|
3630
3662
|
};
|
|
3631
3663
|
|
|
3632
3664
|
var ja = {
|
|
@@ -3656,7 +3688,7 @@ var ja = {
|
|
|
3656
3688
|
formatPainter: '形式ペインタ',
|
|
3657
3689
|
link: 'リンク',
|
|
3658
3690
|
hr: '区切り線',
|
|
3659
|
-
video: '
|
|
3691
|
+
video: 'YouTube',
|
|
3660
3692
|
codeBlock: 'コードブロック',
|
|
3661
3693
|
heading: '見出し',
|
|
3662
3694
|
heading1: '見出し 1',
|
|
@@ -3678,6 +3710,7 @@ var ja = {
|
|
|
3678
3710
|
file: 'ファイル',
|
|
3679
3711
|
emoji: '絵文字',
|
|
3680
3712
|
equation: '数式',
|
|
3713
|
+
twitter: 'ツイート',
|
|
3681
3714
|
removeColor: 'デフォルト',
|
|
3682
3715
|
},
|
|
3683
3716
|
slash: {
|
|
@@ -3717,10 +3750,12 @@ var ja = {
|
|
|
3717
3750
|
hrDesc: '水平線を挿入',
|
|
3718
3751
|
codeBlock: 'コードブロック',
|
|
3719
3752
|
codeBlockDesc: 'コードブロックを挿入',
|
|
3720
|
-
video: '
|
|
3753
|
+
video: 'YouTube',
|
|
3721
3754
|
videoDesc: 'YouTube から動画を挿入',
|
|
3722
3755
|
equation: '数式',
|
|
3723
3756
|
equationDesc: 'TeX 数式を挿入',
|
|
3757
|
+
twitter: 'ツイート',
|
|
3758
|
+
twitterDesc: 'ツイートを挿入',
|
|
3724
3759
|
image: '画像',
|
|
3725
3760
|
imageDesc: '画像をアップロード',
|
|
3726
3761
|
file: 'ファイル',
|
|
@@ -3796,6 +3831,13 @@ var ja = {
|
|
|
3796
3831
|
help: 'サポートされている機能',
|
|
3797
3832
|
placeholder: 'TeX 数式を入力してください',
|
|
3798
3833
|
},
|
|
3834
|
+
twitter: {
|
|
3835
|
+
embed: 'ツイートを埋め込む',
|
|
3836
|
+
remove: '削除',
|
|
3837
|
+
description: '下の入力欄に X (Twitter) リンクを貼り付けてください。',
|
|
3838
|
+
url: 'リンク',
|
|
3839
|
+
urlError: '有効なリンクを入力してください。',
|
|
3840
|
+
},
|
|
3799
3841
|
};
|
|
3800
3842
|
|
|
3801
3843
|
var ko = {
|
|
@@ -3825,7 +3867,7 @@ var ko = {
|
|
|
3825
3867
|
formatPainter: '형식 복사기',
|
|
3826
3868
|
link: '링크',
|
|
3827
3869
|
hr: '구분선',
|
|
3828
|
-
video: '
|
|
3870
|
+
video: '유튜브',
|
|
3829
3871
|
codeBlock: '코드 블록',
|
|
3830
3872
|
heading: '제목',
|
|
3831
3873
|
heading1: '제목 1',
|
|
@@ -3847,6 +3889,7 @@ var ko = {
|
|
|
3847
3889
|
file: '파일',
|
|
3848
3890
|
emoji: '이모지',
|
|
3849
3891
|
equation: '수학 공식',
|
|
3892
|
+
twitter: '트윗',
|
|
3850
3893
|
removeColor: '기본 색상',
|
|
3851
3894
|
},
|
|
3852
3895
|
slash: {
|
|
@@ -3873,7 +3916,7 @@ var ko = {
|
|
|
3873
3916
|
checklist: '체크리스트',
|
|
3874
3917
|
checklistDesc: '체크리스트를 작성',
|
|
3875
3918
|
table: '표',
|
|
3876
|
-
tableDesc: '표를
|
|
3919
|
+
tableDesc: '표를 추가',
|
|
3877
3920
|
infoAlert: '정보 블록',
|
|
3878
3921
|
infoAlertDesc: '정보 블록을 작성',
|
|
3879
3922
|
tipAlert: '팁 블록',
|
|
@@ -3883,13 +3926,15 @@ var ko = {
|
|
|
3883
3926
|
dangerAlert: '위험 블록',
|
|
3884
3927
|
dangerAlertDesc: '위험 블록을 작성',
|
|
3885
3928
|
hr: '구분선',
|
|
3886
|
-
hrDesc: '구분선을
|
|
3929
|
+
hrDesc: '구분선을 추가',
|
|
3887
3930
|
codeBlock: '코드 블록',
|
|
3888
|
-
codeBlockDesc: '코드 블록을
|
|
3889
|
-
video: '
|
|
3890
|
-
videoDesc: '유튜브 동영상을
|
|
3931
|
+
codeBlockDesc: '코드 블록을 추가',
|
|
3932
|
+
video: '유튜브',
|
|
3933
|
+
videoDesc: '유튜브 동영상을 추가',
|
|
3891
3934
|
equation: '수학 공식',
|
|
3892
|
-
equationDesc: 'TeX 수식을
|
|
3935
|
+
equationDesc: 'TeX 수식을 추가',
|
|
3936
|
+
twitter: '트윗',
|
|
3937
|
+
twitterDesc: '트윗을 추가',
|
|
3893
3938
|
image: '이미지',
|
|
3894
3939
|
imageDesc: '이미지를 업로드',
|
|
3895
3940
|
file: '파일',
|
|
@@ -3908,12 +3953,12 @@ var ko = {
|
|
|
3908
3953
|
fitTable: '페이지 너비에 맞게 조정',
|
|
3909
3954
|
cellBackground: '셀 배경 색상',
|
|
3910
3955
|
column: '열',
|
|
3911
|
-
insertColumnLeft: '왼쪽에 열을
|
|
3912
|
-
insertColumnRight: '오른쪽에 열을
|
|
3956
|
+
insertColumnLeft: '왼쪽에 열을 추가',
|
|
3957
|
+
insertColumnRight: '오른쪽에 열을 추가',
|
|
3913
3958
|
deleteColumn: '열을 삭제',
|
|
3914
3959
|
row: '행',
|
|
3915
|
-
insertRowAbove: '위에 행을
|
|
3916
|
-
insertRowBelow: '아래에 행을
|
|
3960
|
+
insertRowAbove: '위에 행을 추가',
|
|
3961
|
+
insertRowBelow: '아래에 행을 추가',
|
|
3917
3962
|
deleteRow: '행을 삭제',
|
|
3918
3963
|
merge: '셀을 병합',
|
|
3919
3964
|
mergeUp: '위쪽으로 셀을 병합',
|
|
@@ -3951,9 +3996,9 @@ var ko = {
|
|
|
3951
3996
|
remove: '삭제',
|
|
3952
3997
|
},
|
|
3953
3998
|
video: {
|
|
3954
|
-
embed: '
|
|
3999
|
+
embed: '동영상을 추가',
|
|
3955
4000
|
remove: '삭제',
|
|
3956
|
-
description: '아래 입력란에
|
|
4001
|
+
description: '아래 입력란에 유튜브 링크를 붙여넣으세요.',
|
|
3957
4002
|
url: '링크',
|
|
3958
4003
|
urlError: '유효한 링크를 입력하세요.',
|
|
3959
4004
|
},
|
|
@@ -3965,6 +4010,13 @@ var ko = {
|
|
|
3965
4010
|
help: '지원되는 기능',
|
|
3966
4011
|
placeholder: 'TeX 수식을 입력하세요',
|
|
3967
4012
|
},
|
|
4013
|
+
twitter: {
|
|
4014
|
+
embed: '트윗을 추가',
|
|
4015
|
+
remove: '삭제',
|
|
4016
|
+
description: '아래 입력란에 X (Twitter) 링크를 붙여넣으세요.',
|
|
4017
|
+
url: '링크',
|
|
4018
|
+
urlError: '유효한 링크를 입력하세요.',
|
|
4019
|
+
},
|
|
3968
4020
|
};
|
|
3969
4021
|
|
|
3970
4022
|
const localeTranslations = {
|
|
@@ -4687,9 +4739,7 @@ class Box {
|
|
|
4687
4739
|
if (component === undefined) {
|
|
4688
4740
|
throw new Error(`Box "${node}" has not been defined yet.`);
|
|
4689
4741
|
}
|
|
4690
|
-
|
|
4691
|
-
const name = encode(component.name);
|
|
4692
|
-
this.node = query(template `<lake-box type="${type}" name="${name}"></lake-box>`);
|
|
4742
|
+
this.node = query(template `<lake-box type="${component.type}" name="${component.name}"></lake-box>`);
|
|
4693
4743
|
if (component.value) {
|
|
4694
4744
|
this.value = component.value;
|
|
4695
4745
|
}
|
|
@@ -4906,6 +4956,281 @@ function getBox(boxNode) {
|
|
|
4906
4956
|
return box;
|
|
4907
4957
|
}
|
|
4908
4958
|
|
|
4959
|
+
// The CornerToolbar class represents a button group located in the top-right corner of a box.
|
|
4960
|
+
class CornerToolbar {
|
|
4961
|
+
constructor(config) {
|
|
4962
|
+
this.config = config;
|
|
4963
|
+
this.locale = this.config.locale || i18nObject('en-US');
|
|
4964
|
+
this.root = query(config.root);
|
|
4965
|
+
this.container = query('<div class="lake-corner-toolbar" />');
|
|
4966
|
+
}
|
|
4967
|
+
appendButton(item) {
|
|
4968
|
+
const buttonNode = query(template `
|
|
4969
|
+
<button type="button" name="${item.name}" tabindex="-1" />
|
|
4970
|
+
`);
|
|
4971
|
+
const tooltip = typeof item.tooltip === 'string' ? item.tooltip : item.tooltip(this.locale);
|
|
4972
|
+
buttonNode.attr('title', tooltip);
|
|
4973
|
+
if (item.icon) {
|
|
4974
|
+
buttonNode.append(item.icon);
|
|
4975
|
+
}
|
|
4976
|
+
this.container.append(buttonNode);
|
|
4977
|
+
buttonNode.on('click', event => {
|
|
4978
|
+
event.preventDefault();
|
|
4979
|
+
item.onClick(event);
|
|
4980
|
+
});
|
|
4981
|
+
}
|
|
4982
|
+
render() {
|
|
4983
|
+
const { items } = this.config;
|
|
4984
|
+
if (items.length === 0) {
|
|
4985
|
+
return;
|
|
4986
|
+
}
|
|
4987
|
+
this.root.append(this.container);
|
|
4988
|
+
for (const item of items) {
|
|
4989
|
+
this.appendButton(item);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
|
|
4994
|
+
// The Resizer class represents a UI component used to resize images or videos.
|
|
4995
|
+
class Resizer {
|
|
4996
|
+
constructor(config) {
|
|
4997
|
+
this.config = config;
|
|
4998
|
+
this.root = query(config.root);
|
|
4999
|
+
this.target = query(config.target);
|
|
5000
|
+
this.container = query(template `
|
|
5001
|
+
<div class="lake-resizer">
|
|
5002
|
+
<div class="lake-resizer-top-left"></div>
|
|
5003
|
+
<div class="lake-resizer-top-right"></div>
|
|
5004
|
+
<div class="lake-resizer-bottom-left"></div>
|
|
5005
|
+
<div class="lake-resizer-bottom-right"></div>
|
|
5006
|
+
<div class="lake-resizer-info"></div>
|
|
5007
|
+
</div>
|
|
5008
|
+
`);
|
|
5009
|
+
}
|
|
5010
|
+
bindEvents(pointerNode) {
|
|
5011
|
+
const target = this.target;
|
|
5012
|
+
const infoNode = this.container.find('.lake-resizer-info');
|
|
5013
|
+
const isPlus = pointerNode.attr('class').indexOf('-right') >= 0;
|
|
5014
|
+
const initialWidth = target.width();
|
|
5015
|
+
const initialHeight = target.height();
|
|
5016
|
+
const rate = initialHeight / initialWidth;
|
|
5017
|
+
let clientX = 0;
|
|
5018
|
+
let width = 0;
|
|
5019
|
+
// resizing box
|
|
5020
|
+
const pointermoveListener = (event) => {
|
|
5021
|
+
const pointerEvent = event;
|
|
5022
|
+
const diffX = pointerEvent.clientX - clientX;
|
|
5023
|
+
const newWidth = Math.round(isPlus ? width + diffX : width - diffX);
|
|
5024
|
+
const newHeight = Math.round(rate * newWidth);
|
|
5025
|
+
infoNode.text(`${newWidth} x ${newHeight}`);
|
|
5026
|
+
target.css({
|
|
5027
|
+
width: `${newWidth}px`,
|
|
5028
|
+
height: `${newHeight}px`,
|
|
5029
|
+
});
|
|
5030
|
+
if (this.config.onResize) {
|
|
5031
|
+
this.config.onResize(newWidth, newHeight);
|
|
5032
|
+
}
|
|
5033
|
+
};
|
|
5034
|
+
// start resizing
|
|
5035
|
+
const pointerdownListener = (event) => {
|
|
5036
|
+
const pointerEvent = event;
|
|
5037
|
+
const pointerNativeNode = pointerNode.get(0);
|
|
5038
|
+
// The capture will be implicitly released after a pointerup or pointercancel event.
|
|
5039
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
|
|
5040
|
+
try {
|
|
5041
|
+
// Test case throws an exception on Firefox.
|
|
5042
|
+
pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
|
|
5043
|
+
}
|
|
5044
|
+
catch ( /* empty */_a) { /* empty */ }
|
|
5045
|
+
clientX = pointerEvent.clientX;
|
|
5046
|
+
width = target.width();
|
|
5047
|
+
infoNode.text(`${initialWidth} x ${initialHeight}`);
|
|
5048
|
+
infoNode.show();
|
|
5049
|
+
pointerNode.on('pointermove', pointermoveListener);
|
|
5050
|
+
};
|
|
5051
|
+
// stop resizing
|
|
5052
|
+
const pointerupListner = () => {
|
|
5053
|
+
pointerNode.off('pointermove');
|
|
5054
|
+
infoNode.hide();
|
|
5055
|
+
width = target.width();
|
|
5056
|
+
const height = Math.round(rate * width);
|
|
5057
|
+
this.config.onStop(width, height);
|
|
5058
|
+
};
|
|
5059
|
+
// cancel resizing
|
|
5060
|
+
const pointercancelListner = () => {
|
|
5061
|
+
pointerNode.off('pointermove');
|
|
5062
|
+
infoNode.hide();
|
|
5063
|
+
};
|
|
5064
|
+
pointerNode.on('pointerdown', pointerdownListener);
|
|
5065
|
+
pointerNode.on('pointerup', pointerupListner);
|
|
5066
|
+
pointerNode.on('pointercancel', pointercancelListner);
|
|
5067
|
+
}
|
|
5068
|
+
render() {
|
|
5069
|
+
this.bindEvents(this.container.find('.lake-resizer-top-left'));
|
|
5070
|
+
this.bindEvents(this.container.find('.lake-resizer-top-right'));
|
|
5071
|
+
this.bindEvents(this.container.find('.lake-resizer-bottom-left'));
|
|
5072
|
+
this.bindEvents(this.container.find('.lake-resizer-bottom-right'));
|
|
5073
|
+
this.root.append(this.container);
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
|
|
5077
|
+
/**
|
|
5078
|
+
* Returns the localized string.
|
|
5079
|
+
*/
|
|
5080
|
+
function getLocaleString(locale, value) {
|
|
5081
|
+
return typeof value === 'string' ? value : value(locale);
|
|
5082
|
+
}
|
|
5083
|
+
/**
|
|
5084
|
+
* Appends a corner toolbar to the iframe box.
|
|
5085
|
+
*/
|
|
5086
|
+
function appendCornerToolbar(config, box) {
|
|
5087
|
+
const editor = box.getEditor();
|
|
5088
|
+
const boxContainer = box.getContainer();
|
|
5089
|
+
const rootNode = boxContainer.find('.lake-iframe');
|
|
5090
|
+
if (rootNode.find('.lake-corner-toolbar').length > 0) {
|
|
5091
|
+
return;
|
|
5092
|
+
}
|
|
5093
|
+
new CornerToolbar({
|
|
5094
|
+
locale: editor.locale,
|
|
5095
|
+
root: rootNode,
|
|
5096
|
+
items: [
|
|
5097
|
+
{
|
|
5098
|
+
name: 'remove',
|
|
5099
|
+
icon: icons.get('remove'),
|
|
5100
|
+
tooltip: config.deleteButtonText,
|
|
5101
|
+
onClick: event => {
|
|
5102
|
+
event.stopPropagation();
|
|
5103
|
+
editor.selection.removeBox(box);
|
|
5104
|
+
editor.history.save();
|
|
5105
|
+
},
|
|
5106
|
+
},
|
|
5107
|
+
],
|
|
5108
|
+
}).render();
|
|
5109
|
+
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Shows the iframe in the box.
|
|
5112
|
+
*/
|
|
5113
|
+
function showIframe(config, box) {
|
|
5114
|
+
const editor = box.getEditor();
|
|
5115
|
+
const boxContainer = box.getContainer();
|
|
5116
|
+
const value = box.value;
|
|
5117
|
+
const width = value.width || config.width;
|
|
5118
|
+
const height = value.height || config.height;
|
|
5119
|
+
const iframeNode = query('<iframe></iframe>');
|
|
5120
|
+
iframeNode.css({
|
|
5121
|
+
width,
|
|
5122
|
+
height,
|
|
5123
|
+
});
|
|
5124
|
+
const iframeAttributes = config.iframeAttributes(value.url);
|
|
5125
|
+
for (const key of Object.keys(iframeAttributes)) {
|
|
5126
|
+
iframeNode.attr(key, iframeAttributes[key]);
|
|
5127
|
+
}
|
|
5128
|
+
const placeholderNode = query('<div class="lake-iframe-placeholder" />');
|
|
5129
|
+
placeholderNode.css({
|
|
5130
|
+
width,
|
|
5131
|
+
height,
|
|
5132
|
+
});
|
|
5133
|
+
if (config.iframePlaceholder) {
|
|
5134
|
+
placeholderNode.append(config.iframePlaceholder);
|
|
5135
|
+
}
|
|
5136
|
+
const rootNode = boxContainer.find('.lake-iframe');
|
|
5137
|
+
iframeNode.on('load', () => {
|
|
5138
|
+
placeholderNode.remove();
|
|
5139
|
+
if (editor.readonly) {
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
if (config.resize === true && rootNode.find('.lake-resizer').length === 0) {
|
|
5143
|
+
new Resizer({
|
|
5144
|
+
root: rootNode,
|
|
5145
|
+
target: iframeNode,
|
|
5146
|
+
onStop: (newWidth, newHeight) => {
|
|
5147
|
+
box.updateValue({
|
|
5148
|
+
width: `${newWidth}px`,
|
|
5149
|
+
height: `${newHeight}px`,
|
|
5150
|
+
});
|
|
5151
|
+
editor.selection.selectBox(box);
|
|
5152
|
+
editor.history.save();
|
|
5153
|
+
},
|
|
5154
|
+
}).render();
|
|
5155
|
+
}
|
|
5156
|
+
});
|
|
5157
|
+
if (config.validUrl(value.url)) {
|
|
5158
|
+
rootNode.prepend(iframeNode);
|
|
5159
|
+
}
|
|
5160
|
+
else {
|
|
5161
|
+
placeholderNode.css('position', 'static');
|
|
5162
|
+
}
|
|
5163
|
+
rootNode.prepend(placeholderNode);
|
|
5164
|
+
if (config.beforeIframeLoad) {
|
|
5165
|
+
config.beforeIframeLoad(box);
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
/**
|
|
5169
|
+
* Creates an iframe box component with configurable properties.
|
|
5170
|
+
* This component supports rendering an iframe with customizable attributes, resizing, and toolbar functionalities.
|
|
5171
|
+
*/
|
|
5172
|
+
function createIframeBox(config) {
|
|
5173
|
+
return {
|
|
5174
|
+
type: config.type,
|
|
5175
|
+
name: config.name,
|
|
5176
|
+
render: box => {
|
|
5177
|
+
const editor = box.getEditor();
|
|
5178
|
+
const locale = editor.locale;
|
|
5179
|
+
const value = box.value;
|
|
5180
|
+
const boxContainer = box.getContainer();
|
|
5181
|
+
const rootNode = query('<div class="lake-iframe" />');
|
|
5182
|
+
boxContainer.empty();
|
|
5183
|
+
boxContainer.append(rootNode);
|
|
5184
|
+
if (value.url === undefined) {
|
|
5185
|
+
if (editor.readonly) {
|
|
5186
|
+
box.node.hide();
|
|
5187
|
+
return;
|
|
5188
|
+
}
|
|
5189
|
+
const formNode = query(template `
|
|
5190
|
+
<div class="lake-iframe-form">
|
|
5191
|
+
<div class="lake-description">${getLocaleString(locale, config.formDescription)}</div>
|
|
5192
|
+
<div class="lake-input-label">${getLocaleString(locale, config.urlLabel || '')}</div>
|
|
5193
|
+
<div class="lake-input-field">
|
|
5194
|
+
<input type="text" name="url" placeholder="${config.urlPlaceholder}" />
|
|
5195
|
+
</div>
|
|
5196
|
+
</div>
|
|
5197
|
+
`);
|
|
5198
|
+
const button = new Button({
|
|
5199
|
+
root: formNode.find('.lake-input-field'),
|
|
5200
|
+
name: 'embed',
|
|
5201
|
+
type: 'primary',
|
|
5202
|
+
text: getLocaleString(locale, config.embedButtonText),
|
|
5203
|
+
onClick: () => {
|
|
5204
|
+
const url = formNode.find('input[name="url"]').value();
|
|
5205
|
+
if (!config.validUrl(url)) {
|
|
5206
|
+
editor.config.onMessage('error', getLocaleString(locale, config.urlError));
|
|
5207
|
+
return;
|
|
5208
|
+
}
|
|
5209
|
+
box.updateValue('url', url);
|
|
5210
|
+
editor.history.save();
|
|
5211
|
+
formNode.remove();
|
|
5212
|
+
showIframe(config, box);
|
|
5213
|
+
},
|
|
5214
|
+
});
|
|
5215
|
+
formNode.find('input[name="url"]').on('keydown', event => {
|
|
5216
|
+
if (isKeyHotkey('enter', event)) {
|
|
5217
|
+
event.preventDefault();
|
|
5218
|
+
button.node.emit('click');
|
|
5219
|
+
}
|
|
5220
|
+
});
|
|
5221
|
+
button.render();
|
|
5222
|
+
rootNode.append(formNode);
|
|
5223
|
+
}
|
|
5224
|
+
else {
|
|
5225
|
+
showIframe(config, box);
|
|
5226
|
+
}
|
|
5227
|
+
if (!editor.readonly) {
|
|
5228
|
+
appendCornerToolbar(config, box);
|
|
5229
|
+
}
|
|
5230
|
+
},
|
|
5231
|
+
};
|
|
5232
|
+
}
|
|
5233
|
+
|
|
4909
5234
|
/**
|
|
4910
5235
|
* The Fragment interface represents a lightweight document object that has no parent.
|
|
4911
5236
|
* It is designed for efficient manipulation of document structures without affecting the main DOM.
|
|
@@ -6273,7 +6598,7 @@ function removeBox(range) {
|
|
|
6273
6598
|
return box;
|
|
6274
6599
|
}
|
|
6275
6600
|
|
|
6276
|
-
var version = "0.3.
|
|
6601
|
+
var version = "0.3.10";
|
|
6277
6602
|
|
|
6278
6603
|
// Converts the custom HTML tags to the special tags that can not be parsed by browser.
|
|
6279
6604
|
function denormalizeValue(value) {
|
|
@@ -7938,6 +8263,28 @@ class Editor {
|
|
|
7938
8263
|
}
|
|
7939
8264
|
});
|
|
7940
8265
|
}
|
|
8266
|
+
/**
|
|
8267
|
+
* Binds events for pointer.
|
|
8268
|
+
*/
|
|
8269
|
+
bindPointerEvents() {
|
|
8270
|
+
this.container.on('pointerdown', event => {
|
|
8271
|
+
const pointerEvent = event;
|
|
8272
|
+
if (pointerEvent.target !== null && pointerEvent.target !== this.container.get(0)) {
|
|
8273
|
+
return;
|
|
8274
|
+
}
|
|
8275
|
+
const lastChild = this.container.last();
|
|
8276
|
+
if (lastChild.isTable || lastChild.isBlockBox) {
|
|
8277
|
+
const lastChildRect = lastChild.get(0).getBoundingClientRect();
|
|
8278
|
+
if (pointerEvent.clientY > lastChildRect.bottom) {
|
|
8279
|
+
pointerEvent.preventDefault();
|
|
8280
|
+
const paragraph = query('<p><br /></p>');
|
|
8281
|
+
lastChild.after(paragraph);
|
|
8282
|
+
this.selection.range.shrinkBefore(paragraph);
|
|
8283
|
+
this.selection.sync();
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
});
|
|
8287
|
+
}
|
|
7941
8288
|
/**
|
|
7942
8289
|
* Returns translation functions for the specified language.
|
|
7943
8290
|
*/
|
|
@@ -8161,6 +8508,7 @@ class Editor {
|
|
|
8161
8508
|
document.addEventListener('click', this.clickListener);
|
|
8162
8509
|
this.bindInputEvents();
|
|
8163
8510
|
this.bindHistoryEvents();
|
|
8511
|
+
this.bindPointerEvents();
|
|
8164
8512
|
}
|
|
8165
8513
|
}
|
|
8166
8514
|
/**
|
|
@@ -10659,124 +11007,6 @@ var codeBlock = (editor) => {
|
|
|
10659
11007
|
});
|
|
10660
11008
|
};
|
|
10661
11009
|
|
|
10662
|
-
// The CornerToolbar class represents a button group located in the top-right corner of a box.
|
|
10663
|
-
class CornerToolbar {
|
|
10664
|
-
constructor(config) {
|
|
10665
|
-
this.config = config;
|
|
10666
|
-
this.locale = this.config.locale || i18nObject('en-US');
|
|
10667
|
-
this.root = query(config.root);
|
|
10668
|
-
this.container = query('<div class="lake-corner-toolbar" />');
|
|
10669
|
-
}
|
|
10670
|
-
appendButton(item) {
|
|
10671
|
-
const buttonNode = query(template `
|
|
10672
|
-
<button type="button" name="${item.name}" tabindex="-1" />
|
|
10673
|
-
`);
|
|
10674
|
-
const tooltip = typeof item.tooltip === 'string' ? item.tooltip : item.tooltip(this.locale);
|
|
10675
|
-
buttonNode.attr('title', tooltip);
|
|
10676
|
-
if (item.icon) {
|
|
10677
|
-
buttonNode.append(item.icon);
|
|
10678
|
-
}
|
|
10679
|
-
this.container.append(buttonNode);
|
|
10680
|
-
buttonNode.on('click', event => {
|
|
10681
|
-
event.preventDefault();
|
|
10682
|
-
item.onClick(event);
|
|
10683
|
-
});
|
|
10684
|
-
}
|
|
10685
|
-
render() {
|
|
10686
|
-
const { items } = this.config;
|
|
10687
|
-
if (items.length === 0) {
|
|
10688
|
-
return;
|
|
10689
|
-
}
|
|
10690
|
-
this.root.append(this.container);
|
|
10691
|
-
for (const item of items) {
|
|
10692
|
-
this.appendButton(item);
|
|
10693
|
-
}
|
|
10694
|
-
}
|
|
10695
|
-
}
|
|
10696
|
-
|
|
10697
|
-
// The Resizer class represents a UI component used to resize images or videos.
|
|
10698
|
-
class Resizer {
|
|
10699
|
-
constructor(config) {
|
|
10700
|
-
this.config = config;
|
|
10701
|
-
this.root = query(config.root);
|
|
10702
|
-
this.target = query(config.target);
|
|
10703
|
-
this.container = query(template `
|
|
10704
|
-
<div class="lake-resizer">
|
|
10705
|
-
<div class="lake-resizer-top-left"></div>
|
|
10706
|
-
<div class="lake-resizer-top-right"></div>
|
|
10707
|
-
<div class="lake-resizer-bottom-left"></div>
|
|
10708
|
-
<div class="lake-resizer-bottom-right"></div>
|
|
10709
|
-
<div class="lake-resizer-info"></div>
|
|
10710
|
-
</div>
|
|
10711
|
-
`);
|
|
10712
|
-
}
|
|
10713
|
-
bindEvents(pointerNode) {
|
|
10714
|
-
const target = this.target;
|
|
10715
|
-
const infoNode = this.container.find('.lake-resizer-info');
|
|
10716
|
-
const isPlus = pointerNode.attr('class').indexOf('-right') >= 0;
|
|
10717
|
-
const initialWidth = target.width();
|
|
10718
|
-
const initialHeight = target.height();
|
|
10719
|
-
const rate = initialHeight / initialWidth;
|
|
10720
|
-
let clientX = 0;
|
|
10721
|
-
let width = 0;
|
|
10722
|
-
// resizing box
|
|
10723
|
-
const pointermoveListener = (event) => {
|
|
10724
|
-
const pointerEvent = event;
|
|
10725
|
-
const diffX = pointerEvent.clientX - clientX;
|
|
10726
|
-
const newWidth = Math.round(isPlus ? width + diffX : width - diffX);
|
|
10727
|
-
const newHeight = Math.round(rate * newWidth);
|
|
10728
|
-
infoNode.text(`${newWidth} x ${newHeight}`);
|
|
10729
|
-
target.css({
|
|
10730
|
-
width: `${newWidth}px`,
|
|
10731
|
-
height: `${newHeight}px`,
|
|
10732
|
-
});
|
|
10733
|
-
if (this.config.onResize) {
|
|
10734
|
-
this.config.onResize(newWidth, newHeight);
|
|
10735
|
-
}
|
|
10736
|
-
};
|
|
10737
|
-
// start resizing
|
|
10738
|
-
const pointerdownListener = (event) => {
|
|
10739
|
-
const pointerEvent = event;
|
|
10740
|
-
const pointerNativeNode = pointerNode.get(0);
|
|
10741
|
-
// The capture will be implicitly released after a pointerup or pointercancel event.
|
|
10742
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
|
|
10743
|
-
try {
|
|
10744
|
-
// Test case throws an exception on Firefox.
|
|
10745
|
-
pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
|
|
10746
|
-
}
|
|
10747
|
-
catch ( /* empty */_a) { /* empty */ }
|
|
10748
|
-
clientX = pointerEvent.clientX;
|
|
10749
|
-
width = target.width();
|
|
10750
|
-
infoNode.text(`${initialWidth} x ${initialHeight}`);
|
|
10751
|
-
infoNode.show();
|
|
10752
|
-
pointerNode.on('pointermove', pointermoveListener);
|
|
10753
|
-
};
|
|
10754
|
-
// stop resizing
|
|
10755
|
-
const pointerupListner = () => {
|
|
10756
|
-
pointerNode.off('pointermove');
|
|
10757
|
-
infoNode.hide();
|
|
10758
|
-
width = target.width();
|
|
10759
|
-
const height = Math.round(rate * width);
|
|
10760
|
-
this.config.onStop(width, height);
|
|
10761
|
-
};
|
|
10762
|
-
// cancel resizing
|
|
10763
|
-
const pointercancelListner = () => {
|
|
10764
|
-
pointerNode.off('pointermove');
|
|
10765
|
-
infoNode.hide();
|
|
10766
|
-
};
|
|
10767
|
-
pointerNode.on('pointerdown', pointerdownListener);
|
|
10768
|
-
pointerNode.on('pointerup', pointerupListner);
|
|
10769
|
-
pointerNode.on('pointercancel', pointercancelListner);
|
|
10770
|
-
}
|
|
10771
|
-
render() {
|
|
10772
|
-
this.bindEvents(this.container.find('.lake-resizer-top-left'));
|
|
10773
|
-
this.bindEvents(this.container.find('.lake-resizer-top-right'));
|
|
10774
|
-
this.bindEvents(this.container.find('.lake-resizer-bottom-left'));
|
|
10775
|
-
this.bindEvents(this.container.find('.lake-resizer-bottom-right'));
|
|
10776
|
-
this.root.append(this.container);
|
|
10777
|
-
}
|
|
10778
|
-
}
|
|
10779
|
-
|
|
10780
11010
|
const alignValueMap = {
|
|
10781
11011
|
start: 'left',
|
|
10782
11012
|
end: 'right',
|
|
@@ -11405,158 +11635,36 @@ var image = (editor) => {
|
|
|
11405
11635
|
});
|
|
11406
11636
|
};
|
|
11407
11637
|
|
|
11408
|
-
|
|
11409
|
-
|
|
11638
|
+
/**
|
|
11639
|
+
* Extracts ID from the specified URL.
|
|
11640
|
+
*/
|
|
11641
|
+
function getId$1(url) {
|
|
11642
|
+
const result = /[\w\-]+$/.exec(url || '');
|
|
11410
11643
|
return result ? result[0] : '';
|
|
11411
11644
|
}
|
|
11412
|
-
|
|
11413
|
-
const editor = box.getEditor();
|
|
11414
|
-
const boxContainer = box.getContainer();
|
|
11415
|
-
const rootNode = boxContainer.find('.lake-video');
|
|
11416
|
-
new CornerToolbar({
|
|
11417
|
-
locale: editor.locale,
|
|
11418
|
-
root: rootNode,
|
|
11419
|
-
items: [
|
|
11420
|
-
{
|
|
11421
|
-
name: 'remove',
|
|
11422
|
-
icon: icons.get('remove'),
|
|
11423
|
-
tooltip: editor.locale.video.remove(),
|
|
11424
|
-
onClick: event => {
|
|
11425
|
-
event.stopPropagation();
|
|
11426
|
-
editor.selection.removeBox(box);
|
|
11427
|
-
editor.history.save();
|
|
11428
|
-
},
|
|
11429
|
-
},
|
|
11430
|
-
],
|
|
11431
|
-
}).render();
|
|
11432
|
-
}
|
|
11433
|
-
function showVideo(box) {
|
|
11434
|
-
const editor = box.getEditor();
|
|
11435
|
-
const boxContainer = box.getContainer();
|
|
11436
|
-
const value = box.value;
|
|
11437
|
-
const width = value.width || 560;
|
|
11438
|
-
const height = value.height || 315;
|
|
11439
|
-
boxContainer.css({
|
|
11440
|
-
width: `${width}px`,
|
|
11441
|
-
height: `${height}px`,
|
|
11442
|
-
});
|
|
11443
|
-
const videoId = getVideoId(value.url);
|
|
11444
|
-
if (videoId === '') {
|
|
11445
|
-
throw new Error(`Invalid link: ${value.url}`);
|
|
11446
|
-
}
|
|
11447
|
-
// YouTube URL: https://www.youtube.com/watch?v=5sMBhDv4sik
|
|
11448
|
-
// The script for embedding YouTube:
|
|
11449
|
-
// <iframe width="560" height="315" src="https://www.youtube.com/embed/5sMBhDv4sik" title="YouTube video player"
|
|
11450
|
-
// frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
11451
|
-
// referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
|
11452
|
-
const iframeNode = query(template `
|
|
11453
|
-
<iframe width="100%" height="${height}" src="https://www.youtube.com/embed/${videoId}" title="YouTube video player"
|
|
11454
|
-
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
11455
|
-
referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
|
11456
|
-
`);
|
|
11457
|
-
const rootNode = boxContainer.find('.lake-video');
|
|
11458
|
-
if (!editor.readonly) {
|
|
11459
|
-
iframeNode.on('load', () => {
|
|
11460
|
-
appendButtonGroup(box);
|
|
11461
|
-
new CornerToolbar({
|
|
11462
|
-
locale: editor.locale,
|
|
11463
|
-
root: rootNode,
|
|
11464
|
-
items: [
|
|
11465
|
-
{
|
|
11466
|
-
name: 'remove',
|
|
11467
|
-
icon: icons.get('remove'),
|
|
11468
|
-
tooltip: editor.locale.video.remove(),
|
|
11469
|
-
onClick: event => {
|
|
11470
|
-
event.stopPropagation();
|
|
11471
|
-
editor.selection.removeBox(box);
|
|
11472
|
-
editor.history.save();
|
|
11473
|
-
},
|
|
11474
|
-
},
|
|
11475
|
-
],
|
|
11476
|
-
}).render();
|
|
11477
|
-
new Resizer({
|
|
11478
|
-
root: rootNode,
|
|
11479
|
-
target: boxContainer,
|
|
11480
|
-
onResize: (newWidth, newHeight) => {
|
|
11481
|
-
iframeNode.attr({
|
|
11482
|
-
height: newHeight.toString(),
|
|
11483
|
-
});
|
|
11484
|
-
},
|
|
11485
|
-
onStop: (newWidth, newHeight) => {
|
|
11486
|
-
box.updateValue({
|
|
11487
|
-
width: newWidth,
|
|
11488
|
-
height: newHeight,
|
|
11489
|
-
});
|
|
11490
|
-
editor.history.save();
|
|
11491
|
-
},
|
|
11492
|
-
}).render();
|
|
11493
|
-
});
|
|
11494
|
-
}
|
|
11495
|
-
rootNode.append(iframeNode);
|
|
11496
|
-
}
|
|
11497
|
-
var videoBox = {
|
|
11645
|
+
const videoBox = createIframeBox({
|
|
11498
11646
|
type: 'inline',
|
|
11499
11647
|
name: 'video',
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
<div class="lake-row">${locale.video.url()}</div>
|
|
11521
|
-
<div class="lake-row">
|
|
11522
|
-
<input type="text" name="url" placeholder="https://www.youtube.com/watch?v=..." />
|
|
11523
|
-
</div>
|
|
11524
|
-
<div class="lake-row lake-button-row"></div>
|
|
11525
|
-
</div>
|
|
11526
|
-
`);
|
|
11527
|
-
const button = new Button({
|
|
11528
|
-
root: formNode.find('.lake-button-row'),
|
|
11529
|
-
name: 'embed',
|
|
11530
|
-
type: 'primary',
|
|
11531
|
-
text: locale.video.embed(),
|
|
11532
|
-
onClick: () => {
|
|
11533
|
-
const url = formNode.find('input[name="url"]').value();
|
|
11534
|
-
if (url.indexOf('https://www.youtube.com/') < 0 || getVideoId(url) === '') {
|
|
11535
|
-
editor.config.onMessage('error', locale.video.urlError());
|
|
11536
|
-
return;
|
|
11537
|
-
}
|
|
11538
|
-
box.updateValue('url', url);
|
|
11539
|
-
editor.history.save();
|
|
11540
|
-
formNode.remove();
|
|
11541
|
-
showVideo(box);
|
|
11542
|
-
},
|
|
11543
|
-
});
|
|
11544
|
-
formNode.find('input[name="url"]').on('keydown', event => {
|
|
11545
|
-
if (isKeyHotkey('enter', event)) {
|
|
11546
|
-
event.preventDefault();
|
|
11547
|
-
button.node.emit('click');
|
|
11548
|
-
}
|
|
11549
|
-
});
|
|
11550
|
-
button.render();
|
|
11551
|
-
rootNode.append(formNode);
|
|
11552
|
-
appendButtonGroup(box);
|
|
11553
|
-
}
|
|
11554
|
-
else {
|
|
11555
|
-
showVideo(box);
|
|
11556
|
-
}
|
|
11557
|
-
},
|
|
11558
|
-
};
|
|
11559
|
-
|
|
11648
|
+
width: '560px',
|
|
11649
|
+
height: '315px',
|
|
11650
|
+
formDescription: locale => locale.video.description(),
|
|
11651
|
+
urlLabel: locale => locale.video.url(),
|
|
11652
|
+
urlPlaceholder: 'https://www.youtube.com/watch?v=...',
|
|
11653
|
+
embedButtonText: locale => locale.video.embed(),
|
|
11654
|
+
deleteButtonText: locale => locale.video.remove(),
|
|
11655
|
+
validUrl: url => url.indexOf('https://www.youtube.com/') === 0 && getId$1(url) !== '',
|
|
11656
|
+
urlError: locale => locale.video.urlError(),
|
|
11657
|
+
iframePlaceholder: icons.get('video'),
|
|
11658
|
+
iframeAttributes: url => ({
|
|
11659
|
+
src: `https://www.youtube.com/embed/${getId$1(url)}`,
|
|
11660
|
+
title: 'YouTube video player',
|
|
11661
|
+
frameborder: '0',
|
|
11662
|
+
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share',
|
|
11663
|
+
referrerpolicy: 'strict-origin-when-cross-origin',
|
|
11664
|
+
allowfullscreen: 'true',
|
|
11665
|
+
}),
|
|
11666
|
+
resize: true,
|
|
11667
|
+
});
|
|
11560
11668
|
var video = (editor) => {
|
|
11561
11669
|
if (editor.readonly) {
|
|
11562
11670
|
return;
|
|
@@ -11566,7 +11674,10 @@ var video = (editor) => {
|
|
|
11566
11674
|
const box = editor.selection.insertBox('video', value);
|
|
11567
11675
|
editor.history.save();
|
|
11568
11676
|
if (box) {
|
|
11569
|
-
box.getContainer().find('input[name="url"]')
|
|
11677
|
+
const urlInput = box.getContainer().find('input[name="url"]');
|
|
11678
|
+
if (urlInput.length > 0) {
|
|
11679
|
+
urlInput.focus();
|
|
11680
|
+
}
|
|
11570
11681
|
}
|
|
11571
11682
|
},
|
|
11572
11683
|
});
|
|
@@ -12287,6 +12398,81 @@ var mention = (editor) => {
|
|
|
12287
12398
|
};
|
|
12288
12399
|
};
|
|
12289
12400
|
|
|
12401
|
+
/**
|
|
12402
|
+
* Extracts ID from the specified URL.
|
|
12403
|
+
*/
|
|
12404
|
+
function getId(url) {
|
|
12405
|
+
const result = /\d+$/.exec(url || '');
|
|
12406
|
+
return result ? result[0] : '';
|
|
12407
|
+
}
|
|
12408
|
+
/**
|
|
12409
|
+
* Returns the current theme.
|
|
12410
|
+
*/
|
|
12411
|
+
function getTheme() {
|
|
12412
|
+
return document.documentElement.classList.contains('lake-dark') ? 'dark' : 'light';
|
|
12413
|
+
}
|
|
12414
|
+
const twitterBox = createIframeBox({
|
|
12415
|
+
type: 'inline',
|
|
12416
|
+
name: 'twitter',
|
|
12417
|
+
width: '550px',
|
|
12418
|
+
height: '300px',
|
|
12419
|
+
formDescription: locale => locale.twitter.description(),
|
|
12420
|
+
urlLabel: locale => locale.twitter.url(),
|
|
12421
|
+
urlPlaceholder: 'https://x.com/username/status/...',
|
|
12422
|
+
embedButtonText: locale => locale.twitter.embed(),
|
|
12423
|
+
deleteButtonText: locale => locale.twitter.remove(),
|
|
12424
|
+
validUrl: url => (url.indexOf('https://x.com/') === 0 || url.indexOf('https://twitter.com/') === 0) && getId(url) !== '',
|
|
12425
|
+
urlError: locale => locale.twitter.urlError(),
|
|
12426
|
+
iframePlaceholder: icons.get('twitter'),
|
|
12427
|
+
iframeAttributes: url => {
|
|
12428
|
+
return {
|
|
12429
|
+
src: `https://platform.twitter.com/embed/Tweet.html?id=${getId(url)}&theme=${getTheme()}`,
|
|
12430
|
+
title: 'Twitter tweet',
|
|
12431
|
+
scrolling: 'no',
|
|
12432
|
+
frameborder: '0',
|
|
12433
|
+
allowtransparency: 'true',
|
|
12434
|
+
allowfullscreen: 'true',
|
|
12435
|
+
};
|
|
12436
|
+
},
|
|
12437
|
+
beforeIframeLoad: box => {
|
|
12438
|
+
const boxContainer = box.getContainer();
|
|
12439
|
+
const placeholder = boxContainer.find('.lake-iframe-placeholder');
|
|
12440
|
+
const iframe = boxContainer.find('iframe');
|
|
12441
|
+
if (getTheme() === 'dark') {
|
|
12442
|
+
iframe.css('border-radius', '13px');
|
|
12443
|
+
}
|
|
12444
|
+
const messageListener = (event) => {
|
|
12445
|
+
if (event.origin === 'https://platform.twitter.com') {
|
|
12446
|
+
const params = event.data['twttr.embed'].params;
|
|
12447
|
+
const height = params[0].height;
|
|
12448
|
+
if (height > 0) {
|
|
12449
|
+
placeholder.css('height', `${height}px`);
|
|
12450
|
+
iframe.css('height', `${height}px`);
|
|
12451
|
+
window.removeEventListener('message', messageListener);
|
|
12452
|
+
}
|
|
12453
|
+
}
|
|
12454
|
+
};
|
|
12455
|
+
window.addEventListener('message', messageListener);
|
|
12456
|
+
},
|
|
12457
|
+
});
|
|
12458
|
+
var twitter = (editor) => {
|
|
12459
|
+
if (editor.readonly) {
|
|
12460
|
+
return;
|
|
12461
|
+
}
|
|
12462
|
+
editor.command.add('twitter', {
|
|
12463
|
+
execute: (value) => {
|
|
12464
|
+
const box = editor.selection.insertBox('twitter', value);
|
|
12465
|
+
editor.history.save();
|
|
12466
|
+
if (box) {
|
|
12467
|
+
const urlInput = box.getContainer().find('input[name="url"]');
|
|
12468
|
+
if (urlInput.length > 0) {
|
|
12469
|
+
urlInput.focus();
|
|
12470
|
+
}
|
|
12471
|
+
}
|
|
12472
|
+
},
|
|
12473
|
+
});
|
|
12474
|
+
};
|
|
12475
|
+
|
|
12290
12476
|
const headingTypeMap = new Map([
|
|
12291
12477
|
['#', 'h1'],
|
|
12292
12478
|
['##', 'h2'],
|
|
@@ -13479,6 +13665,16 @@ const slashItems = [
|
|
|
13479
13665
|
editor.command.execute(value);
|
|
13480
13666
|
},
|
|
13481
13667
|
},
|
|
13668
|
+
{
|
|
13669
|
+
name: 'twitter',
|
|
13670
|
+
type: 'button',
|
|
13671
|
+
icon: icons.get('twitter'),
|
|
13672
|
+
title: locale => locale.slash.twitter(),
|
|
13673
|
+
description: locale => locale.slash.twitterDesc(),
|
|
13674
|
+
onClick: (editor, value) => {
|
|
13675
|
+
editor.command.execute(value);
|
|
13676
|
+
},
|
|
13677
|
+
},
|
|
13482
13678
|
{
|
|
13483
13679
|
name: 'image',
|
|
13484
13680
|
type: 'upload',
|
|
@@ -13726,6 +13922,7 @@ Editor.box.add(imageBox);
|
|
|
13726
13922
|
Editor.box.add(videoBox);
|
|
13727
13923
|
Editor.box.add(fileBox);
|
|
13728
13924
|
Editor.box.add(mentionBox);
|
|
13925
|
+
Editor.box.add(twitterBox);
|
|
13729
13926
|
Editor.plugin.add('copy', copy);
|
|
13730
13927
|
Editor.plugin.add('cut', cut);
|
|
13731
13928
|
Editor.plugin.add('paste', paste);
|
|
@@ -13762,6 +13959,7 @@ Editor.plugin.add('emoji', emoji);
|
|
|
13762
13959
|
Editor.plugin.add('equation', equation);
|
|
13763
13960
|
Editor.plugin.add('specialCharacter', specialCharacter);
|
|
13764
13961
|
Editor.plugin.add('mention', mention);
|
|
13962
|
+
Editor.plugin.add('twitter', twitter);
|
|
13765
13963
|
Editor.plugin.add('markdown', markdown);
|
|
13766
13964
|
Editor.plugin.add('enterKey', enterKey);
|
|
13767
13965
|
Editor.plugin.add('shiftEnterKey', shiftEnterKey);
|
|
@@ -13772,5 +13970,5 @@ Editor.plugin.add('arrowKeys', arrowKeys);
|
|
|
13772
13970
|
Editor.plugin.add('escapeKey', escapeKey);
|
|
13773
13971
|
Editor.plugin.add('slash', slash);
|
|
13774
13972
|
|
|
13775
|
-
export { Box, Button, Dropdown, Editor, Fragment, HTMLParser, Nodes, Range, TextParser, Toolbar, addMark, deleteContents, getBox, getContentRules, icons, insertBlock, insertBookmark, insertBox, insertContents, query, removeBox, removeMark, setBlocks, splitBlock$1 as splitBlock, splitMarks, template, toBookmark, toHex };
|
|
13973
|
+
export { Box, Button, Dropdown, Editor, Fragment, HTMLParser, Nodes, Range, TextParser, Toolbar, addMark, createIframeBox, deleteContents, getBox, getContentRules, icons, insertBlock, insertBookmark, insertBox, insertContents, query, removeBox, removeMark, setBlocks, splitBlock$1 as splitBlock, splitMarks, template, toBookmark, toHex };
|
|
13776
13974
|
//# sourceMappingURL=lake.js.map
|