lakelib 0.1.21 → 0.1.23

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.
Files changed (41) hide show
  1. package/README.md +4 -4
  2. package/dist/lake.css +45 -36
  3. package/dist/lake.min.js +37 -29
  4. package/dist/lake.min.js.map +1 -1
  5. package/lib/lake.css +45 -36
  6. package/lib/lake.js +989 -299
  7. package/lib/lake.js.map +1 -1
  8. package/lib/types/boxes/code-block.d.ts +2 -1
  9. package/lib/types/boxes/emoji.d.ts +2 -1
  10. package/lib/types/boxes/equation.d.ts +2 -1
  11. package/lib/types/boxes/file.d.ts +2 -1
  12. package/lib/types/boxes/hr.d.ts +2 -1
  13. package/lib/types/boxes/image.d.ts +2 -1
  14. package/lib/types/boxes/video.d.ts +2 -1
  15. package/lib/types/config/slash-items.d.ts +2 -0
  16. package/lib/types/css/index.d.ts +1 -1
  17. package/lib/types/editor.d.ts +3 -0
  18. package/lib/types/i18n/en-US/index.d.ts +36 -0
  19. package/lib/types/i18n/ja/index.d.ts +36 -0
  20. package/lib/types/i18n/ko/index.d.ts +36 -0
  21. package/lib/types/i18n/types.d.ts +278 -2
  22. package/lib/types/i18n/zh-CN/index.d.ts +36 -0
  23. package/lib/types/index.d.ts +3 -2
  24. package/lib/types/managers/keystroke.d.ts +0 -1
  25. package/lib/types/managers/plugin.d.ts +4 -5
  26. package/lib/types/managers/selection.d.ts +1 -1
  27. package/lib/types/models/box.d.ts +1 -1
  28. package/lib/types/models/nodes.d.ts +2 -0
  29. package/lib/types/plugins/slash.d.ts +3 -0
  30. package/lib/types/types/plugin.d.ts +3 -0
  31. package/lib/types/types/{commands.d.ts → slash.d.ts} +4 -4
  32. package/lib/types/ui/box-toolbar.d.ts +2 -1
  33. package/lib/types/ui/dropdown.d.ts +1 -0
  34. package/lib/types/ui/link-popup.d.ts +2 -3
  35. package/lib/types/ui/slash-popup.d.ts +34 -0
  36. package/lib/types/ui/toolbar.d.ts +5 -3
  37. package/lib/types/utils/index.d.ts +2 -1
  38. package/lib/types/utils/{node-and-view.d.ts → node-position.d.ts} +1 -1
  39. package/lib/types/utils/scroll-to-node.d.ts +2 -0
  40. package/package.json +1 -1
  41. package/lib/types/ui/commands-popup.d.ts +0 -24
package/lib/lake.js CHANGED
@@ -1098,6 +1098,15 @@ class Nodes {
1098
1098
  element.innerText = value;
1099
1099
  });
1100
1100
  }
1101
+ value(value) {
1102
+ if (value === undefined) {
1103
+ const inputElement = this.get(0);
1104
+ return inputElement.value;
1105
+ }
1106
+ return this.eachElement(element => {
1107
+ element.value = value;
1108
+ });
1109
+ }
1101
1110
  outerHTML() {
1102
1111
  const element = this.get(0);
1103
1112
  return element.outerHTML;
@@ -1506,7 +1515,7 @@ class Range {
1506
1515
  selectBox(boxNode) {
1507
1516
  const boxContainer = boxNode.find('.lake-box-container');
1508
1517
  if (boxContainer.length === 0) {
1509
- throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1518
+ throw new Error(`The box "${boxNode.attr('name')}" (id=${boxNode.id}) cannot be selected because it has not been rendered yet.`);
1510
1519
  }
1511
1520
  this.setStart(boxContainer, 0);
1512
1521
  this.collapseToStart();
@@ -1515,7 +1524,7 @@ class Range {
1515
1524
  selectBoxStart(boxNode) {
1516
1525
  const boxStrip = boxNode.find('.lake-box-strip');
1517
1526
  if (boxStrip.length === 0) {
1518
- throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1527
+ throw new Error(`The box "${boxNode.attr('name')}" (id=${boxNode.id}) cannot be selected because it has not been rendered yet.`);
1519
1528
  }
1520
1529
  this.selectNodeContents(boxStrip.eq(0));
1521
1530
  this.collapseToStart();
@@ -1524,7 +1533,7 @@ class Range {
1524
1533
  selectBoxEnd(boxNode) {
1525
1534
  const boxStrip = boxNode.find('.lake-box-strip');
1526
1535
  if (boxStrip.length === 0) {
1527
- throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1536
+ throw new Error(`The box "${boxNode.attr('name')}" (id=${boxNode.id}) cannot be selected because it has not been rendered yet.`);
1528
1537
  }
1529
1538
  this.selectNodeContents(boxStrip.eq(1));
1530
1539
  this.collapseToStart();
@@ -2770,7 +2779,7 @@ function morph(node, otherNode, config = {}) {
2770
2779
  }
2771
2780
 
2772
2781
  // Returns an object that indicates the specified node's position relative to the viewport.
2773
- function nodeAndView(node) {
2782
+ function nodePosition(node) {
2774
2783
  const nativeNode = node.get(0);
2775
2784
  const rect = nativeNode.getBoundingClientRect();
2776
2785
  let left = rect.left;
@@ -2801,6 +2810,15 @@ function nodeAndView(node) {
2801
2810
  return position;
2802
2811
  }
2803
2812
 
2813
+ // If a node is not visible, scrolls the container that contains this node to its position to make it visible.
2814
+ // If the node is visible, then scrolling takes place.
2815
+ function scrollToNode(node, options) {
2816
+ const position = nodePosition(node);
2817
+ if (position.left < 0 || position.right < 0 || position.top < 0 || position.bottom < 0) {
2818
+ node.get(0).scrollIntoView(options);
2819
+ }
2820
+ }
2821
+
2804
2822
  const boxInstances = new Map();
2805
2823
  function getInstanceMap(id) {
2806
2824
  let instanceMap = boxInstances.get(id);
@@ -2916,6 +2934,42 @@ var enUS = {
2916
2934
  equation: 'Equation',
2917
2935
  removeColor: 'Remove color',
2918
2936
  },
2937
+ slash: {
2938
+ heading1: 'Heading 1',
2939
+ heading1Desc: 'Create a heading level 1',
2940
+ heading2: 'Heading 2',
2941
+ heading2Desc: 'Create a heading level 2',
2942
+ heading3: 'Heading 3',
2943
+ heading3Desc: 'Create a heading level 3',
2944
+ heading4: 'Heading 4',
2945
+ heading4Desc: 'Create a heading level 4',
2946
+ heading5: 'Heading 5',
2947
+ heading5Desc: 'Create a heading level 5',
2948
+ heading6: 'Heading 6',
2949
+ heading6Desc: 'Create a heading level 6',
2950
+ paragraph: 'Paragraph',
2951
+ paragraphDesc: 'Create a paragraph',
2952
+ blockQuote: 'Block quotation',
2953
+ blockQuoteDesc: 'Create a block quotation',
2954
+ numberedList: 'Numbered list',
2955
+ numberedListDesc: 'Create a numbered list',
2956
+ bulletedList: 'Bulleted list',
2957
+ bulletedListDesc: 'Create a bulleted list',
2958
+ checklist: 'Checklist',
2959
+ checklistDesc: 'Create a checklist',
2960
+ hr: 'Horizontal line',
2961
+ hrDesc: 'Insert a horizontal line',
2962
+ codeBlock: 'Code block',
2963
+ codeBlockDesc: 'Insert a code block',
2964
+ video: 'Video',
2965
+ videoDesc: 'Insert a video from YouTube',
2966
+ equation: 'Equation',
2967
+ equationDesc: 'Insert TeX expression in text',
2968
+ image: 'Image',
2969
+ imageDesc: 'Upload an image',
2970
+ file: 'File',
2971
+ fileDesc: 'Upload a file',
2972
+ },
2919
2973
  link: {
2920
2974
  newLink: 'New link',
2921
2975
  url: 'Link URL',
@@ -3005,6 +3059,42 @@ var zhCN = {
3005
3059
  equation: '公式',
3006
3060
  removeColor: '默认',
3007
3061
  },
3062
+ slash: {
3063
+ heading1: '标题 1',
3064
+ heading1Desc: '创建标题 1',
3065
+ heading2: '标题 2',
3066
+ heading2Desc: '创建标题 2',
3067
+ heading3: '标题 3',
3068
+ heading3Desc: '创建标题 3',
3069
+ heading4: '标题 4',
3070
+ heading4Desc: '创建标题 4',
3071
+ heading5: '标题 5',
3072
+ heading5Desc: '创建标题 5',
3073
+ heading6: '标题 6',
3074
+ heading6Desc: '创建标题 6',
3075
+ paragraph: '正文',
3076
+ paragraphDesc: '把当前段落改成正文',
3077
+ blockQuote: '引用',
3078
+ blockQuoteDesc: '创建引用',
3079
+ numberedList: '编号',
3080
+ numberedListDesc: '创建编号列表',
3081
+ bulletedList: '项目符号',
3082
+ bulletedListDesc: '创建项目符号列表',
3083
+ checklist: '任务列表',
3084
+ checklistDesc: '创建任务列表',
3085
+ hr: '分割线',
3086
+ hrDesc: '插入分割线',
3087
+ codeBlock: '代码块',
3088
+ codeBlockDesc: '插入代码块',
3089
+ video: '视频',
3090
+ videoDesc: '插入 YouTube 视频',
3091
+ equation: '公式',
3092
+ equationDesc: '支持 TeX 语法',
3093
+ image: '图片',
3094
+ imageDesc: '上传图片',
3095
+ file: '文件',
3096
+ fileDesc: '上传文件',
3097
+ },
3008
3098
  link: {
3009
3099
  newLink: '新链接',
3010
3100
  url: '链接 URL',
@@ -3050,7 +3140,7 @@ var ja = {
3050
3140
  redo: `やり直し (${modifierText('mod+Y')})`,
3051
3141
  selectAll: `すべて選択 (${modifierText('mod+A')})`,
3052
3142
  paragraph: 'テキスト',
3053
- blockQuote: 'ブロック引用',
3143
+ blockQuote: '引用ブロック',
3054
3144
  numberedList: '番号付きリスト',
3055
3145
  bulletedList: '箇条書きリスト',
3056
3146
  checklist: 'タスクリスト',
@@ -3094,6 +3184,42 @@ var ja = {
3094
3184
  equation: '公式',
3095
3185
  removeColor: 'デフォルト',
3096
3186
  },
3187
+ slash: {
3188
+ heading1: 'タイトル 1',
3189
+ heading1Desc: 'レベル 1 のタイトルを作成',
3190
+ heading2: 'タイトル 2',
3191
+ heading2Desc: 'レベル 2 のタイトルを作成',
3192
+ heading3: 'タイトル 3',
3193
+ heading3Desc: 'レベル 3 のタイトルを作成',
3194
+ heading4: 'タイトル 4',
3195
+ heading4Desc: 'レベル 4 のタイトルを作成',
3196
+ heading5: 'タイトル 5',
3197
+ heading5Desc: 'レベル 5 のタイトルを作成',
3198
+ heading6: 'タイトル 6',
3199
+ heading6Desc: 'レベル 6 のタイトルを作成',
3200
+ paragraph: 'テキスト',
3201
+ paragraphDesc: '段落を作成',
3202
+ blockQuote: '引用ブロック',
3203
+ blockQuoteDesc: '引用ブロックを作成',
3204
+ numberedList: '番号付きリスト',
3205
+ numberedListDesc: '番号付きリストを作成',
3206
+ bulletedList: '箇条書きリスト',
3207
+ bulletedListDesc: '箇条書きリストを作成',
3208
+ checklist: 'タスクリスト',
3209
+ checklistDesc: 'タスクリストを作成',
3210
+ hr: '区切り線',
3211
+ hrDesc: '水平線を挿入',
3212
+ codeBlock: 'コードブロック',
3213
+ codeBlockDesc: 'コードブロックを挿入',
3214
+ video: '動画',
3215
+ videoDesc: 'YouTube から動画を挿入',
3216
+ equation: '公式',
3217
+ equationDesc: 'TeX 式を挿入',
3218
+ image: '画像',
3219
+ imageDesc: '画像をアップロード',
3220
+ file: 'ファイル',
3221
+ fileDesc: 'ファイルをアップロード',
3222
+ },
3097
3223
  link: {
3098
3224
  newLink: '新しいリンク',
3099
3225
  url: 'リンク URL',
@@ -3183,6 +3309,42 @@ var ko = {
3183
3309
  equation: '수식',
3184
3310
  removeColor: '기본색',
3185
3311
  },
3312
+ slash: {
3313
+ heading1: '제목 1',
3314
+ heading1Desc: '1 단계 제목을 작성',
3315
+ heading2: '제목 2',
3316
+ heading2Desc: '2 단계 제목을 작성',
3317
+ heading3: '제목 3',
3318
+ heading3Desc: '3 단계 제목을 작성',
3319
+ heading4: '제목 4',
3320
+ heading4Desc: '4 단계 제목을 작성',
3321
+ heading5: '제목 5',
3322
+ heading5Desc: '5 단계 제목을 작성',
3323
+ heading6: '제목 6',
3324
+ heading6Desc: '6 단계 제목을 작성',
3325
+ paragraph: '텍스트',
3326
+ paragraphDesc: '단락을 작성',
3327
+ blockQuote: '인용문',
3328
+ blockQuoteDesc: '인용문을 작성',
3329
+ numberedList: '순서 목록',
3330
+ numberedListDesc: '순서 목록을 작성',
3331
+ bulletedList: '비순서 목록',
3332
+ bulletedListDesc: '비순서 목록을 작성',
3333
+ checklist: '체크리스트',
3334
+ checklistDesc: '체크리스트를 작성',
3335
+ hr: '구분선',
3336
+ hrDesc: '구분선을 삽입',
3337
+ codeBlock: '코드 블록',
3338
+ codeBlockDesc: '코드 블록을 삽입',
3339
+ video: '동영상',
3340
+ videoDesc: '유튜브 동영상을 삽입',
3341
+ equation: '수식',
3342
+ equationDesc: '텍스 표현식을 삽입',
3343
+ image: '이미지',
3344
+ imageDesc: '이미지를 업로드',
3345
+ file: '파일',
3346
+ fileDesc: '파일을 업로드',
3347
+ },
3186
3348
  link: {
3187
3349
  newLink: '새 링크',
3188
3350
  url: '링크 URL',
@@ -3256,12 +3418,10 @@ class Dropdown {
3256
3418
  this.documentClickListener = (event) => {
3257
3419
  const targetNode = new Nodes(event.target);
3258
3420
  const titleNode = this.node.find('.lake-dropdown-title');
3259
- const menuNode = this.node.find('.lake-dropdown-menu');
3260
3421
  if (targetNode.closest('.lake-dropdown-title').get(0) === titleNode.get(0)) {
3261
3422
  return;
3262
3423
  }
3263
- menuNode.hide();
3264
- document.removeEventListener('click', this.documentClickListener);
3424
+ this.hideMenu();
3265
3425
  };
3266
3426
  this.config = config;
3267
3427
  this.root = config.root;
@@ -3323,6 +3483,15 @@ class Dropdown {
3323
3483
  `;
3324
3484
  const listNode = query(listContent);
3325
3485
  menuNode.append(listNode);
3486
+ listNode.on('mouseenter', () => {
3487
+ if (listNode.hasClass('lake-dropdown-item-selected')) {
3488
+ return;
3489
+ }
3490
+ listNode.addClass('lake-dropdown-item-hovered');
3491
+ });
3492
+ listNode.on('mouseleave', () => {
3493
+ listNode.removeClass('lake-dropdown-item-hovered');
3494
+ });
3326
3495
  if (config.menuType === 'character') {
3327
3496
  listNode.attr('title', menuText);
3328
3497
  listNode.find('.lake-dropdown-menu-text').text(menuItem.value);
@@ -3355,15 +3524,6 @@ class Dropdown {
3355
3524
  menuNode.find('.lake-dropdown-menu-check').css('visibility', 'hidden');
3356
3525
  menuNode.find('li').each(node => {
3357
3526
  const listNode = query(node);
3358
- listNode.on('mouseenter', () => {
3359
- if (listNode.hasClass('lake-dropdown-item-selected')) {
3360
- return;
3361
- }
3362
- listNode.addClass('lake-dropdown-item-hovered');
3363
- });
3364
- listNode.on('mouseleave', () => {
3365
- listNode.removeClass('lake-dropdown-item-hovered');
3366
- });
3367
3527
  if (currentValues.indexOf(listNode.attr('value')) >= 0) {
3368
3528
  listNode.find('.lake-dropdown-menu-check').css('visibility', 'visible');
3369
3529
  }
@@ -3387,6 +3547,12 @@ class Dropdown {
3387
3547
  menuNode.css('visibility', '');
3388
3548
  document.addEventListener('click', this.documentClickListener);
3389
3549
  }
3550
+ hideMenu() {
3551
+ const dropdownNode = this.node;
3552
+ const menuNode = dropdownNode.find('.lake-dropdown-menu');
3553
+ menuNode.hide();
3554
+ document.removeEventListener('click', this.documentClickListener);
3555
+ }
3390
3556
  bindEvents() {
3391
3557
  const config = this.config;
3392
3558
  const dropdownNode = this.node;
@@ -3458,8 +3624,7 @@ class Dropdown {
3458
3624
  this.updateColorAccent(titleNode, value);
3459
3625
  }
3460
3626
  config.onSelect(value);
3461
- menuNode.hide();
3462
- document.removeEventListener('click', this.documentClickListener);
3627
+ this.hideMenu();
3463
3628
  });
3464
3629
  }
3465
3630
  render() {
@@ -3512,8 +3677,8 @@ class Dropdown {
3512
3677
  this.bindEvents();
3513
3678
  }
3514
3679
  unmount() {
3680
+ this.hideMenu();
3515
3681
  this.node.remove();
3516
- document.removeEventListener('click', this.documentClickListener);
3517
3682
  }
3518
3683
  }
3519
3684
 
@@ -3521,13 +3686,13 @@ class BoxToolbar {
3521
3686
  constructor(config) {
3522
3687
  this.buttonItemList = [];
3523
3688
  this.dropdownItemList = [];
3689
+ this.dropdownList = [];
3524
3690
  this.root = query(config.root);
3525
3691
  this.box = config.box;
3526
3692
  this.items = config.items;
3527
3693
  this.locale = config.locale || i18nObject('en-US');
3528
3694
  this.placement = config.placement || 'top';
3529
3695
  this.container = query('<div class="lake-box-toolbar" />');
3530
- this.root.addClass('lake-custom-properties');
3531
3696
  }
3532
3697
  appendDivider() {
3533
3698
  this.container.append('<div class="lake-toolbar-divider" />');
@@ -3565,12 +3730,13 @@ class BoxToolbar {
3565
3730
  },
3566
3731
  });
3567
3732
  dropdown.render();
3733
+ this.dropdownList.push(dropdown);
3568
3734
  }
3569
- updatePosition() {
3735
+ position() {
3570
3736
  const boxNode = this.box.node;
3571
3737
  const boxNativeNode = boxNode.get(0);
3572
3738
  const boxRect = boxNativeNode.getBoundingClientRect();
3573
- const position = nodeAndView(boxNode);
3739
+ const position = nodePosition(boxNode);
3574
3740
  if (position.top < 0 || position.bottom + boxRect.height < 0) {
3575
3741
  this.container.hide();
3576
3742
  return;
@@ -3601,9 +3767,13 @@ class BoxToolbar {
3601
3767
  this.appendDropdown(item);
3602
3768
  }
3603
3769
  }
3604
- this.updatePosition();
3770
+ this.position();
3605
3771
  }
3772
+ // Destroys the toolbar.
3606
3773
  unmount() {
3774
+ for (const dropdown of this.dropdownList) {
3775
+ dropdown.unmount();
3776
+ }
3607
3777
  this.container.remove();
3608
3778
  }
3609
3779
  }
@@ -3705,7 +3875,11 @@ class Box {
3705
3875
  // Returns an instance of the editor that includes the box.
3706
3876
  getEditor() {
3707
3877
  const container = this.node.closest('div[contenteditable]');
3708
- return container.length > 0 ? editors.get(container.id) : undefined;
3878
+ const editor = container.length > 0 ? editors.get(container.id) : undefined;
3879
+ if (!editor) {
3880
+ throw new Error(`The box "${this.name}" (id=${this.node.id}) is not rendered in the editor.`);
3881
+ }
3882
+ return editor;
3709
3883
  }
3710
3884
  // Returns the container node of the box.
3711
3885
  getContainer() {
@@ -3713,11 +3887,15 @@ class Box {
3713
3887
  }
3714
3888
  // Sets a popup toolbar for the box.
3715
3889
  setToolbar(items) {
3716
- const editor = this.getEditor();
3890
+ let editor;
3891
+ try {
3892
+ editor = this.getEditor();
3893
+ }
3894
+ catch ( /* empty */_a) { /* empty */ }
3717
3895
  let toolbar = null;
3718
3896
  const scrollListener = () => {
3719
3897
  if (toolbar) {
3720
- toolbar.updatePosition();
3898
+ toolbar.position();
3721
3899
  }
3722
3900
  };
3723
3901
  this.event.on('focus', () => {
@@ -3859,7 +4037,7 @@ function getBody(xhr) {
3859
4037
  try {
3860
4038
  return JSON.parse(text);
3861
4039
  }
3862
- catch (e) {
4040
+ catch (_a) {
3863
4041
  return text;
3864
4042
  }
3865
4043
  }
@@ -4021,7 +4199,7 @@ var index = /*#__PURE__*/Object.freeze({
4021
4199
  mergeNodes: mergeNodes,
4022
4200
  modifierText: modifierText,
4023
4201
  morph: morph,
4024
- nodeAndView: nodeAndView,
4202
+ nodePosition: nodePosition,
4025
4203
  normalizeValue: normalizeValue,
4026
4204
  parseStyle: parseStyle,
4027
4205
  query: query,
@@ -4029,6 +4207,7 @@ var index = /*#__PURE__*/Object.freeze({
4029
4207
  removeZWS: removeZWS,
4030
4208
  request: request,
4031
4209
  safeTemplate: safeTemplate,
4210
+ scrollToNode: scrollToNode,
4032
4211
  setBlockIndent: setBlockIndent,
4033
4212
  splitNodes: splitNodes,
4034
4213
  template: template,
@@ -5173,7 +5352,7 @@ function removeBox(range) {
5173
5352
  return box;
5174
5353
  }
5175
5354
 
5176
- var version = "0.1.21";
5355
+ var version = "0.1.23";
5177
5356
 
5178
5357
  // Returns the attributes of the element as an key-value object.
5179
5358
  function getAttributes(node) {
@@ -5247,10 +5426,10 @@ class Selection {
5247
5426
  }
5248
5427
  this.selection = selection;
5249
5428
  this.container = container;
5250
- this.range = this.getRangeFromNativeSelection();
5429
+ this.range = this.getCurrentRange();
5251
5430
  }
5252
- // Returns the current selected range from the native selection.
5253
- getRangeFromNativeSelection() {
5431
+ // Returns a range object currently selected.
5432
+ getCurrentRange() {
5254
5433
  if (this.selection.rangeCount > 0) {
5255
5434
  const range = this.selection.getRangeAt(0);
5256
5435
  return new Range(range);
@@ -5264,7 +5443,7 @@ class Selection {
5264
5443
  }
5265
5444
  // Updates the saved range with the range of the native selection.
5266
5445
  updateByRange() {
5267
- const newRange = this.getRangeFromNativeSelection();
5446
+ const newRange = this.getCurrentRange();
5268
5447
  if (!this.container.contains(newRange.commonAncestor)) {
5269
5448
  return;
5270
5449
  }
@@ -5416,6 +5595,11 @@ class Command {
5416
5595
  return commandItem.selectedValues(appliedItems);
5417
5596
  }
5418
5597
  execute(name, ...data) {
5598
+ const container = this.selection.container;
5599
+ const range = this.selection.range;
5600
+ if (!container.contains(range.commonAncestor)) {
5601
+ range.shrinkAfter(container);
5602
+ }
5419
5603
  const commandItem = this.getItem(name);
5420
5604
  commandItem.execute.apply(this, data);
5421
5605
  debug(`Command "${name}" executed`);
@@ -5614,12 +5798,6 @@ class History {
5614
5798
  }
5615
5799
  }
5616
5800
 
5617
- const aliasMap = new Map([
5618
- ['arrow-left', 'left'],
5619
- ['arrow-right', 'right'],
5620
- ['arrow-up', 'up'],
5621
- ['arrow-down', 'down'],
5622
- ]);
5623
5801
  class Keystroke {
5624
5802
  constructor(container) {
5625
5803
  this.keydownEventList = [];
@@ -5630,6 +5808,9 @@ class Keystroke {
5630
5808
  if (keyboardEvent.isComposing) {
5631
5809
  return;
5632
5810
  }
5811
+ if (keyboardEvent.defaultPrevented) {
5812
+ return;
5813
+ }
5633
5814
  for (const item of this.keydownEventList) {
5634
5815
  if (isKeyHotkey(item.type, keyboardEvent)) {
5635
5816
  if (item.listener(keyboardEvent) === false) {
@@ -5643,6 +5824,9 @@ class Keystroke {
5643
5824
  if (keyboardEvent.isComposing) {
5644
5825
  return;
5645
5826
  }
5827
+ if (keyboardEvent.defaultPrevented) {
5828
+ return;
5829
+ }
5646
5830
  for (const item of this.keyupEventList) {
5647
5831
  if (isKeyHotkey(item.type, keyboardEvent)) {
5648
5832
  if (item.listener(keyboardEvent) === false) {
@@ -5652,14 +5836,8 @@ class Keystroke {
5652
5836
  }
5653
5837
  });
5654
5838
  }
5655
- normalizeType(type) {
5656
- var _a;
5657
- type = (_a = aliasMap.get(type)) !== null && _a !== void 0 ? _a : type;
5658
- return type;
5659
- }
5660
5839
  // Sets a keydown shortcut.
5661
5840
  setKeydown(type, listener) {
5662
- type = this.normalizeType(type);
5663
5841
  this.keydownEventList.push({
5664
5842
  type,
5665
5843
  listener,
@@ -5667,7 +5845,6 @@ class Keystroke {
5667
5845
  }
5668
5846
  // Sets a keyup shortcut.
5669
5847
  setKeyup(type, listener) {
5670
- type = this.normalizeType(type);
5671
5848
  this.keyupEventList.push({
5672
5849
  type,
5673
5850
  listener,
@@ -5675,7 +5852,6 @@ class Keystroke {
5675
5852
  }
5676
5853
  // Executes the keydown shortcuts.
5677
5854
  keydown(type) {
5678
- type = this.normalizeType(type);
5679
5855
  for (const item of this.keydownEventList) {
5680
5856
  if (item.type === type) {
5681
5857
  if (item.listener(new KeyboardEvent(type)) === false) {
@@ -5686,7 +5862,6 @@ class Keystroke {
5686
5862
  }
5687
5863
  // Executes the keyup shortcuts.
5688
5864
  keyup(type) {
5689
- type = this.normalizeType(type);
5690
5865
  for (const item of this.keyupEventList) {
5691
5866
  if (item.type === type) {
5692
5867
  if (item.listener(new KeyboardEvent(type)) === false) {
@@ -5711,15 +5886,26 @@ class BoxManager {
5711
5886
 
5712
5887
  class Plugin {
5713
5888
  constructor() {
5714
- this.pluginList = [];
5889
+ this.pluginMap = new Map();
5715
5890
  }
5716
- add(plugin) {
5717
- this.pluginList.push(plugin);
5891
+ add(name, plugin) {
5892
+ if (this.pluginMap.get(name)) {
5893
+ throw new Error(`Plugin "${name}" is already defined.`);
5894
+ }
5895
+ this.pluginMap.set(name, plugin);
5718
5896
  }
5719
5897
  loadAll(editor) {
5720
- for (const plugin of this.pluginList) {
5721
- plugin(editor);
5898
+ const unmountPluginMap = new Map();
5899
+ for (const name of this.pluginMap.keys()) {
5900
+ const plugin = this.pluginMap.get(name);
5901
+ if (plugin && editor.config[name] !== false) {
5902
+ const result = plugin(editor);
5903
+ if (result) {
5904
+ unmountPluginMap.set(name, result);
5905
+ }
5906
+ }
5722
5907
  }
5908
+ return unmountPluginMap;
5723
5909
  }
5724
5910
  }
5725
5911
 
@@ -5749,6 +5935,7 @@ const defaultConfig = {
5749
5935
  console.error(message);
5750
5936
  }
5751
5937
  },
5938
+ slash: false,
5752
5939
  };
5753
5940
  class Editor {
5754
5941
  constructor(config) {
@@ -5760,25 +5947,26 @@ class Editor {
5760
5947
  selectedNameMap: new Map(),
5761
5948
  selectedValuesMap: new Map(),
5762
5949
  };
5950
+ this.unmountPluginMap = new Map();
5763
5951
  this.isComposing = false;
5764
5952
  this.event = new EventEmitter();
5765
5953
  this.box = Editor.box;
5766
5954
  this.copyListener = event => {
5767
- const range = this.selection.range;
5955
+ const range = this.selection.getCurrentRange();
5768
5956
  if (!this.container.contains(range.commonAncestor)) {
5769
5957
  return;
5770
5958
  }
5771
5959
  this.event.emit('copy', event);
5772
5960
  };
5773
5961
  this.cutListener = event => {
5774
- const range = this.selection.range;
5962
+ const range = this.selection.getCurrentRange();
5775
5963
  if (!this.container.contains(range.commonAncestor)) {
5776
5964
  return;
5777
5965
  }
5778
5966
  this.event.emit('cut', event);
5779
5967
  };
5780
5968
  this.pasteListener = event => {
5781
- const range = this.selection.range;
5969
+ const range = this.selection.getCurrentRange();
5782
5970
  if (!this.container.contains(range.commonAncestor)) {
5783
5971
  return;
5784
5972
  }
@@ -5791,7 +5979,7 @@ class Editor {
5791
5979
  };
5792
5980
  this.clickListener = event => {
5793
5981
  const targetNode = new Nodes(event.target);
5794
- if (!targetNode.get(0).isConnected || targetNode.closest('.lake-popup').length > 0) {
5982
+ if (!targetNode.get(0).isConnected) {
5795
5983
  return;
5796
5984
  }
5797
5985
  this.event.emit('click', targetNode);
@@ -5799,6 +5987,9 @@ class Editor {
5799
5987
  this.resizeListener = () => {
5800
5988
  this.event.emit('resize');
5801
5989
  };
5990
+ this.scrollListener = () => {
5991
+ this.event.emit('scroll');
5992
+ };
5802
5993
  this.updateSelectionRange = debounce(() => {
5803
5994
  this.selection.updateByRange();
5804
5995
  }, 1, {
@@ -5974,60 +6165,65 @@ class Editor {
5974
6165
  this.unsavedInputData = '';
5975
6166
  this.unsavedInputCount = 0;
5976
6167
  }
5977
- // Binds events about input.
6168
+ // Handles input event.
6169
+ handleInputEvent(event) {
6170
+ var _a;
6171
+ this.selection.updateByRange();
6172
+ const range = this.selection.range;
6173
+ if (range.isInsideBox) {
6174
+ return;
6175
+ }
6176
+ if (range.isBoxStart || range.isBoxEnd) {
6177
+ this.moveBoxStripText();
6178
+ this.history.save();
6179
+ return;
6180
+ }
6181
+ const inputType = event instanceof CompositionEvent ? 'insertText' : event.inputType;
6182
+ if (inputType === 'insertText') {
6183
+ const inputData = (_a = event.data) !== null && _a !== void 0 ? _a : '';
6184
+ if (inputData.length > 1) {
6185
+ this.history.save({
6186
+ inputType: 'insertText',
6187
+ update: false,
6188
+ });
6189
+ return;
6190
+ }
6191
+ this.unsavedInputData += inputData;
6192
+ this.unsavedInputCount++;
6193
+ if (this.unsavedInputData.length < this.config.minChangeSize) {
6194
+ this.history.save({
6195
+ inputType: 'insertText',
6196
+ update: this.unsavedInputCount > 1,
6197
+ });
6198
+ }
6199
+ else {
6200
+ this.history.save({
6201
+ inputType: 'insertText',
6202
+ update: true,
6203
+ });
6204
+ this.resetUnsavedInputData();
6205
+ }
6206
+ return;
6207
+ }
6208
+ this.history.save();
6209
+ }
6210
+ // Binds events about inputting text.
5978
6211
  bindInputEvents() {
5979
6212
  this.container.on('compositionstart', () => {
5980
6213
  this.isComposing = true;
6214
+ this.container.removeClass('lake-placeholder');
5981
6215
  });
5982
- this.container.on('compositionend', () => {
6216
+ this.container.on('compositionend', event => {
5983
6217
  this.isComposing = false;
6218
+ this.handleInputEvent(event);
5984
6219
  });
5985
6220
  this.container.on('input', event => {
5986
6221
  const inputEvent = event;
5987
- // Here setTimeout is necessary because isComposing is not false after ending composition.
5988
- window.setTimeout(() => {
5989
- var _a;
5990
- const range = this.selection.range;
5991
- if (range.isInsideBox) {
5992
- return;
5993
- }
5994
- // isComposing is false after ending composition because compositionend event has been emitted.
5995
- if (this.isComposing) {
5996
- if (inputEvent.inputType === 'insertCompositionText') {
5997
- this.container.removeClass('lake-placeholder');
5998
- }
5999
- this.event.emit('input', inputEvent);
6000
- return;
6001
- }
6002
- if (range.isBoxStart || range.isBoxEnd) {
6003
- this.moveBoxStripText();
6004
- this.history.save();
6005
- this.event.emit('input', inputEvent);
6006
- return;
6007
- }
6008
- if (inputEvent.inputType === 'insertText' ||
6009
- inputEvent.inputType === 'insertCompositionText') {
6010
- this.unsavedInputData += (_a = inputEvent.data) !== null && _a !== void 0 ? _a : '';
6011
- this.unsavedInputCount++;
6012
- if (this.unsavedInputData.length < this.config.minChangeSize) {
6013
- this.history.save({
6014
- inputType: 'insertText',
6015
- update: this.unsavedInputCount > 1,
6016
- });
6017
- }
6018
- else {
6019
- this.history.save({
6020
- inputType: 'insertText',
6021
- update: true,
6022
- });
6023
- this.resetUnsavedInputData();
6024
- }
6025
- this.event.emit('input', inputEvent);
6026
- return;
6027
- }
6028
- this.history.save();
6029
- this.event.emit('input', inputEvent);
6030
- }, 0);
6222
+ this.isComposing = inputEvent.isComposing;
6223
+ if (this.isComposing) {
6224
+ return;
6225
+ }
6226
+ this.handleInputEvent(event);
6031
6227
  });
6032
6228
  }
6033
6229
  // Binds events about history.
@@ -6058,8 +6254,8 @@ class Editor {
6058
6254
  this.history.event.on('save', (value, options) => {
6059
6255
  this.removeBoxGarbage();
6060
6256
  executeCommonMethods(value);
6061
- this.selection.sync();
6062
6257
  if (options.inputType !== 'insertText') {
6258
+ this.selection.sync();
6063
6259
  this.resetUnsavedInputData();
6064
6260
  }
6065
6261
  });
@@ -6111,7 +6307,7 @@ class Editor {
6111
6307
  }
6112
6308
  // Sets default config for a plugin.
6113
6309
  setPluginConfig(name, config) {
6114
- if (!this.config[name]) {
6310
+ if (typeof this.config[name] !== 'object') {
6115
6311
  this.config[name] = {};
6116
6312
  }
6117
6313
  for (const key of Object.keys(config)) {
@@ -6178,17 +6374,11 @@ class Editor {
6178
6374
  });
6179
6375
  this.overlayContainer.find('.lake-artificial-caret').remove();
6180
6376
  this.overlayContainer.append(artificialCaret);
6181
- const position = nodeAndView(artificialCaret);
6182
- // Scrolls the artificial caret element into the visible area of the browser window
6183
- // if it's not already within the visible area of the browser window.
6184
- // If the element is already within the visibposition.rightle area of the browser window, then no scrolling takes place.
6185
- if (position.left < 0 || position.right < 0 || position.top < 0 || position.bottom < 0) {
6186
- artificialCaret.get(0).scrollIntoView({
6187
- behavior: 'instant',
6188
- block: 'center',
6189
- inline: 'nearest',
6190
- });
6191
- }
6377
+ scrollToNode(artificialCaret, {
6378
+ behavior: 'instant',
6379
+ block: 'center',
6380
+ inline: 'nearest',
6381
+ });
6192
6382
  artificialCaret.remove();
6193
6383
  }
6194
6384
  // Sets the specified value to the editor.
@@ -6221,7 +6411,7 @@ class Editor {
6221
6411
  query(document.body).append(this.popupContainer);
6222
6412
  this.togglePlaceholderClass(htmlParser.getHTML());
6223
6413
  this.container.append(fragment);
6224
- Editor.plugin.loadAll(this);
6414
+ this.unmountPluginMap = Editor.plugin.loadAll(this);
6225
6415
  if (!this.readonly) {
6226
6416
  this.selection.updateByBookmark();
6227
6417
  this.history.save({
@@ -6232,30 +6422,53 @@ class Editor {
6232
6422
  if (this.toolbar) {
6233
6423
  this.toolbar.render(this);
6234
6424
  }
6425
+ this.root.on('scroll', this.scrollListener);
6235
6426
  document.addEventListener('copy', this.copyListener);
6427
+ window.addEventListener('resize', this.resizeListener);
6236
6428
  if (!this.readonly) {
6237
6429
  document.addEventListener('cut', this.cutListener);
6238
6430
  document.addEventListener('paste', this.pasteListener);
6239
6431
  document.addEventListener('selectionchange', this.selectionchangeListener);
6240
6432
  document.addEventListener('click', this.clickListener);
6241
- window.addEventListener('resize', this.resizeListener);
6242
6433
  this.bindInputEvents();
6243
6434
  this.bindHistoryEvents();
6244
6435
  }
6245
6436
  }
6246
- // Destroys the rendered editor.
6437
+ // Destroys the editor.
6247
6438
  unmount() {
6439
+ // Executes delayed executions immediately.
6440
+ this.updateSelectionRange.flush();
6441
+ this.updateBoxSelectionStyle.flush();
6442
+ this.emitStateChangeEvent.flush();
6443
+ for (const name of this.unmountPluginMap.keys()) {
6444
+ const unmountPlugin = this.unmountPluginMap.get(name);
6445
+ if (unmountPlugin) {
6446
+ unmountPlugin();
6447
+ debug(`The plugin "${name}" unmounted`);
6448
+ }
6449
+ }
6450
+ if (this.toolbar) {
6451
+ this.toolbar.unmount();
6452
+ }
6453
+ this.removeBoxGarbage();
6454
+ this.container.find('lake-box').each(boxNativeNode => {
6455
+ const boxNode = query(boxNativeNode);
6456
+ const box = getBox(boxNode);
6457
+ box.unmount();
6458
+ });
6248
6459
  this.event.removeAllListeners();
6249
6460
  this.history.event.removeAllListeners();
6461
+ this.root.off();
6462
+ this.root.removeClass('lake-custom-properties');
6250
6463
  this.root.empty();
6251
6464
  this.popupContainer.remove();
6252
6465
  document.removeEventListener('copy', this.copyListener);
6466
+ window.removeEventListener('resize', this.resizeListener);
6253
6467
  if (!this.readonly) {
6254
6468
  document.removeEventListener('cut', this.cutListener);
6255
6469
  document.removeEventListener('paste', this.pasteListener);
6256
6470
  document.removeEventListener('selectionchange', this.selectionchangeListener);
6257
6471
  document.removeEventListener('click', this.clickListener);
6258
- window.removeEventListener('resize', this.resizeListener);
6259
6472
  }
6260
6473
  }
6261
6474
  }
@@ -6902,7 +7115,7 @@ const toolbarItems = [
6902
7115
  },
6903
7116
  ];
6904
7117
 
6905
- const defaultItems = [
7118
+ const defaultItems$1 = [
6906
7119
  'undo',
6907
7120
  'redo',
6908
7121
  '|',
@@ -6933,8 +7146,9 @@ class Toolbar {
6933
7146
  this.allMenuMap = new Map();
6934
7147
  this.buttonItemList = [];
6935
7148
  this.dropdownItemList = [];
7149
+ this.dropdownList = [];
6936
7150
  this.root = query(config.root);
6937
- this.items = config.items || defaultItems;
7151
+ this.items = config.items || defaultItems$1;
6938
7152
  if (config.placement) {
6939
7153
  this.placement = config.placement;
6940
7154
  }
@@ -6944,7 +7158,7 @@ class Toolbar {
6944
7158
  appendDivider() {
6945
7159
  this.container.append('<div class="lake-toolbar-divider" />');
6946
7160
  }
6947
- appendButton(editor, item) {
7161
+ appendNormalButton(editor, item) {
6948
7162
  const button = new Button({
6949
7163
  root: this.container,
6950
7164
  name: item.name,
@@ -6953,10 +7167,6 @@ class Toolbar {
6953
7167
  tabIndex: -1,
6954
7168
  onClick: () => {
6955
7169
  editor.focus();
6956
- const range = editor.selection.range;
6957
- if (!editor.container.contains(range.commonAncestor)) {
6958
- range.shrinkAfter(editor.container);
6959
- }
6960
7170
  item.onClick(editor, item.name);
6961
7171
  },
6962
7172
  });
@@ -6985,8 +7195,9 @@ class Toolbar {
6985
7195
  },
6986
7196
  });
6987
7197
  dropdown.render();
7198
+ this.dropdownList.push(dropdown);
6988
7199
  }
6989
- appendUpload(editor, item) {
7200
+ appendUploadButton(editor, item) {
6990
7201
  const uploadNode = query(safeTemplate `
6991
7202
  <div class="lake-upload" name="${item.name}">
6992
7203
  <input type="file" />
@@ -7088,7 +7299,6 @@ class Toolbar {
7088
7299
  }
7089
7300
  // Renders a toolbar for the specified editor.
7090
7301
  render(editor) {
7091
- this.root.empty();
7092
7302
  this.root.append(this.container);
7093
7303
  for (const name of this.items) {
7094
7304
  if (name === '|') {
@@ -7107,7 +7317,7 @@ class Toolbar {
7107
7317
  }
7108
7318
  if (item.type === 'button') {
7109
7319
  this.buttonItemList.push(item);
7110
- this.appendButton(editor, item);
7320
+ this.appendNormalButton(editor, item);
7111
7321
  }
7112
7322
  else if (item.type === 'dropdown') {
7113
7323
  this.allMenuMap.set(item.name, Dropdown.getMenuMap(item.menuItems, editor.locale));
@@ -7115,21 +7325,26 @@ class Toolbar {
7115
7325
  this.appendDropdown(editor, item);
7116
7326
  }
7117
7327
  else if (item.type === 'upload') {
7118
- this.appendUpload(editor, item);
7328
+ this.appendUploadButton(editor, item);
7119
7329
  }
7120
7330
  }
7121
7331
  }
7122
7332
  }
7333
+ // Destroys the toolbar.
7334
+ unmount() {
7335
+ for (const dropdown of this.dropdownList) {
7336
+ dropdown.unmount();
7337
+ }
7338
+ this.root.removeClass('lake-custom-properties');
7339
+ this.container.remove();
7340
+ }
7123
7341
  }
7124
7342
 
7125
- const hrBox = {
7343
+ var hrBox = {
7126
7344
  type: 'block',
7127
7345
  name: 'hr',
7128
7346
  render: box => {
7129
7347
  const editor = box.getEditor();
7130
- if (!editor) {
7131
- return;
7132
- }
7133
7348
  const boxContainer = box.getContainer();
7134
7349
  const rootNode = query('<div class="lake-hr"><hr /></div>');
7135
7350
  boxContainer.empty();
@@ -7218,15 +7433,12 @@ function getHighlightStyle(CodeMirror) {
7218
7433
  { tag: [tags.special(tags.variableName)], color: config.special },
7219
7434
  ]);
7220
7435
  }
7221
- const codeBlockBox = {
7436
+ var codeBlockBox = {
7222
7437
  type: 'block',
7223
7438
  name: 'codeBlock',
7224
7439
  render: box => {
7225
7440
  var _a;
7226
7441
  const editor = box.getEditor();
7227
- if (!editor) {
7228
- return;
7229
- }
7230
7442
  const rootNode = query('<div class="lake-code-block" />');
7231
7443
  const boxContainer = box.getContainer();
7232
7444
  boxContainer.css('width', `${editor.container.innerWidth() - 2}px`);
@@ -7265,16 +7477,16 @@ const codeBlockBox = {
7265
7477
  const boxValue = box.value;
7266
7478
  const langItem = langItemMap.get(boxValue.lang);
7267
7479
  const language = new Compartment();
7268
- const changeHandler = (value) => {
7269
- // Here setTimeout is necessary because isComposing is not false after ending composition.
7270
- window.setTimeout(() => {
7271
- if (editor.isComposing) {
7272
- return;
7273
- }
7274
- box.updateValue('code', value);
7275
- editor.history.save();
7276
- }, 0);
7277
- };
7480
+ const changeHandler = debounce((value) => {
7481
+ editor.selection.updateByRange();
7482
+ if (editor.isComposing) {
7483
+ return;
7484
+ }
7485
+ box.updateValue('code', value);
7486
+ editor.history.save();
7487
+ }, 1, {
7488
+ immediate: false,
7489
+ });
7278
7490
  const updateListener = EditorView.updateListener.of((update) => {
7279
7491
  if (!update.docChanged) {
7280
7492
  return;
@@ -7333,6 +7545,7 @@ const codeBlockBox = {
7333
7545
  codeEditor.focus();
7334
7546
  });
7335
7547
  box.event.on('beforeunmount', () => {
7548
+ dropdown.unmount();
7336
7549
  codeEditor.destroy();
7337
7550
  editor.event.off('resize', resizeListener);
7338
7551
  debug('CodeMirror destroyed');
@@ -7410,7 +7623,11 @@ class BoxResizer {
7410
7623
  const pointerNativeNode = pointerNode.get(0);
7411
7624
  // The capture will be implicitly released after a pointerup or pointercancel event.
7412
7625
  // https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
7413
- pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
7626
+ try {
7627
+ // Test case throws an exception on Firefox.
7628
+ pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
7629
+ }
7630
+ catch ( /* empty */_a) { /* empty */ }
7414
7631
  clientX = pointerEvent.clientX;
7415
7632
  width = boxContainer.width();
7416
7633
  infoNode.show();
@@ -7490,9 +7707,6 @@ function getImageInfo(url) {
7490
7707
  // Opens full screen view.
7491
7708
  function openFullScreen(box) {
7492
7709
  const editor = box.getEditor();
7493
- if (!editor) {
7494
- return;
7495
- }
7496
7710
  const dataSource = [];
7497
7711
  let currentIndex = 0;
7498
7712
  const allImageBox = editor.container.find('lake-box[name="image"]');
@@ -7570,20 +7784,11 @@ function openFullScreen(box) {
7570
7784
  }
7571
7785
  return placeholderSrc;
7572
7786
  });
7573
- let savedRange;
7574
7787
  lightbox.on('openingAnimationEnd', () => {
7575
- savedRange = editor.selection.range;
7576
7788
  box.event.emit('openfullscreen');
7577
7789
  });
7578
7790
  lightbox.on('destroy', () => {
7579
- window.setTimeout(() => {
7580
- if (savedRange) {
7581
- // fix(image): lose focus when zooming in the iOS
7582
- editor.selection.range = savedRange;
7583
- editor.selection.sync();
7584
- }
7585
- box.event.emit('closefullscreen');
7586
- }, 0);
7791
+ box.event.emit('closefullscreen');
7587
7792
  });
7588
7793
  lightbox.init();
7589
7794
  lightbox.loadAndOpen(currentIndex);
@@ -7592,9 +7797,6 @@ function openFullScreen(box) {
7592
7797
  function renderError$1(rootNode, box) {
7593
7798
  return __awaiter(this, void 0, void 0, function* () {
7594
7799
  const editor = box.getEditor();
7595
- if (!editor) {
7596
- return;
7597
- }
7598
7800
  const value = box.value;
7599
7801
  box.getContainer().css({
7600
7802
  width: '',
@@ -7628,9 +7830,6 @@ function renderError$1(rootNode, box) {
7628
7830
  function renderUploading(rootNode, box) {
7629
7831
  return __awaiter(this, void 0, void 0, function* () {
7630
7832
  const editor = box.getEditor();
7631
- if (!editor) {
7632
- return;
7633
- }
7634
7833
  const value = box.value;
7635
7834
  const imageInfo = yield getImageInfo(value.url);
7636
7835
  if (!imageInfo.width || !imageInfo.height) {
@@ -7688,9 +7887,6 @@ function renderUploading(rootNode, box) {
7688
7887
  function renderDone(rootNode, box) {
7689
7888
  return __awaiter(this, void 0, void 0, function* () {
7690
7889
  const editor = box.getEditor();
7691
- if (!editor) {
7692
- return;
7693
- }
7694
7890
  const boxContainer = box.getContainer();
7695
7891
  const value = box.value;
7696
7892
  const imageInfo = yield getImageInfo(value.url);
@@ -7758,14 +7954,11 @@ function renderDone(rootNode, box) {
7758
7954
  rootNode.append(imgNode);
7759
7955
  });
7760
7956
  }
7761
- const imageBox = {
7957
+ var imageBox = {
7762
7958
  type: 'inline',
7763
7959
  name: 'image',
7764
7960
  render: box => {
7765
7961
  const editor = box.getEditor();
7766
- if (!editor) {
7767
- return;
7768
- }
7769
7962
  const value = box.value;
7770
7963
  if (editor.readonly && ['uploading', 'loading', 'error'].indexOf(value.status) >= 0) {
7771
7964
  box.node.hide();
@@ -7838,16 +8031,8 @@ function getVideoId(url) {
7838
8031
  const result = /\w+$/i.exec(url || '');
7839
8032
  return result ? result[0] : '';
7840
8033
  }
7841
- function getInputValue(videoNode, name) {
7842
- const inputElement = videoNode.find(`input[name="${name}"]`);
7843
- const nativeInputElement = inputElement.get(0);
7844
- return nativeInputElement.value;
7845
- }
7846
8034
  function appendButtonGroup(box) {
7847
8035
  const editor = box.getEditor();
7848
- if (!editor) {
7849
- return;
7850
- }
7851
8036
  const boxContainer = box.getContainer();
7852
8037
  const videoNode = boxContainer.find('.lake-video');
7853
8038
  const buttonGroupNode = query(safeTemplate `
@@ -7869,9 +8054,6 @@ function appendButtonGroup(box) {
7869
8054
  }
7870
8055
  function showVideo(box) {
7871
8056
  const editor = box.getEditor();
7872
- if (!editor) {
7873
- return;
7874
- }
7875
8057
  const boxContainer = box.getContainer();
7876
8058
  const value = box.value;
7877
8059
  const width = value.width || 560;
@@ -7920,14 +8102,11 @@ function showVideo(box) {
7920
8102
  }
7921
8103
  rootNode.append(iframeNode);
7922
8104
  }
7923
- const videoBox = {
8105
+ var videoBox = {
7924
8106
  type: 'inline',
7925
8107
  name: 'video',
7926
8108
  render: box => {
7927
8109
  const editor = box.getEditor();
7928
- if (!editor) {
7929
- return;
7930
- }
7931
8110
  const locale = editor.locale;
7932
8111
  const value = box.value;
7933
8112
  const boxContainer = box.getContainer();
@@ -7959,7 +8138,7 @@ const videoBox = {
7959
8138
  type: 'primary',
7960
8139
  text: locale.video.embed(),
7961
8140
  onClick: () => {
7962
- const url = getInputValue(formNode, 'url');
8141
+ const url = formNode.find('input[name="url"]').value();
7963
8142
  if (url.indexOf('https://www.youtube.com/') < 0 || getVideoId(url) === '') {
7964
8143
  editor.config.onMessage('error', locale.video.urlError());
7965
8144
  return;
@@ -8003,9 +8182,6 @@ const boxToolbarItems = [
8003
8182
  tooltip: locale => locale.file.remove(),
8004
8183
  onClick: box => {
8005
8184
  const editor = box.getEditor();
8006
- if (!editor) {
8007
- return;
8008
- }
8009
8185
  editor.selection.removeBox(box);
8010
8186
  editor.history.save();
8011
8187
  },
@@ -8013,10 +8189,6 @@ const boxToolbarItems = [
8013
8189
  ];
8014
8190
  function appendContent(rootNode, box) {
8015
8191
  return __awaiter(this, void 0, void 0, function* () {
8016
- const editor = box.getEditor();
8017
- if (!editor) {
8018
- return;
8019
- }
8020
8192
  const value = box.value;
8021
8193
  const infoNode = query(safeTemplate `
8022
8194
  <div class="lake-file-info">
@@ -8047,14 +8219,11 @@ function appendContent(rootNode, box) {
8047
8219
  rootNode.append(infoNode);
8048
8220
  });
8049
8221
  }
8050
- const fileBox = {
8222
+ var fileBox = {
8051
8223
  type: 'inline',
8052
8224
  name: 'file',
8053
8225
  render: box => {
8054
8226
  const editor = box.getEditor();
8055
- if (!editor) {
8056
- return;
8057
- }
8058
8227
  const value = box.value;
8059
8228
  if (editor.readonly && ['uploading', 'error'].indexOf(value.status) >= 0) {
8060
8229
  box.node.hide();
@@ -8081,14 +8250,11 @@ const fileBox = {
8081
8250
  },
8082
8251
  };
8083
8252
 
8084
- const emojiBox = {
8253
+ var emojiBox = {
8085
8254
  type: 'inline',
8086
8255
  name: 'emoji',
8087
8256
  render: box => {
8088
8257
  const editor = box.getEditor();
8089
- if (!editor) {
8090
- return;
8091
- }
8092
8258
  const value = box.value;
8093
8259
  const boxContainer = box.getContainer();
8094
8260
  const rootNode = query(safeTemplate `
@@ -8105,9 +8271,6 @@ const emojiBox = {
8105
8271
  const defaultExpression = String.raw `\sqrt{x}`;
8106
8272
  function renderError(box) {
8107
8273
  const editor = box.getEditor();
8108
- if (!editor) {
8109
- return;
8110
- }
8111
8274
  if (editor.readonly) {
8112
8275
  box.node.hide();
8113
8276
  return;
@@ -8124,14 +8287,11 @@ function renderError(box) {
8124
8287
  Please check if the "katex" library is added to this page.
8125
8288
  `.trim());
8126
8289
  }
8127
- const equationBox = {
8290
+ var equationBox = {
8128
8291
  type: 'inline',
8129
8292
  name: 'equation',
8130
8293
  render: box => {
8131
8294
  const editor = box.getEditor();
8132
- if (!editor) {
8133
- return;
8134
- }
8135
8295
  const rootNode = query('<div class="lake-equation" />');
8136
8296
  const boxContainer = box.getContainer();
8137
8297
  boxContainer.empty();
@@ -8160,10 +8320,9 @@ const equationBox = {
8160
8320
  `);
8161
8321
  rootNode.append(formNode);
8162
8322
  const textareaNode = formNode.find('textarea');
8163
- const textareaNativeNode = textareaNode.get(0);
8164
- textareaNativeNode.value = defaultCode;
8323
+ textareaNode.value(defaultCode);
8165
8324
  textareaNode.on('input', () => {
8166
- const code = textareaNativeNode.value.trim();
8325
+ const code = textareaNode.value().trim();
8167
8326
  viewNode.html(window.katex.renderToString(code || defaultExpression, {
8168
8327
  throwOnError: false,
8169
8328
  }));
@@ -9145,8 +9304,7 @@ var formatPainter = (editor) => {
9145
9304
  if (editor.container.contains(targetNode)) {
9146
9305
  return;
9147
9306
  }
9148
- const buttonNode = targetNode.closest('button[name="formatPainter"]');
9149
- if (buttonNode.length > 0) {
9307
+ if (targetNode.closest('button[name="formatPainter"]').length > 0) {
9150
9308
  return;
9151
9309
  }
9152
9310
  editor.container.removeClass(formatPainterClassName);
@@ -9207,7 +9365,7 @@ class LinkPopup {
9207
9365
  if (!this.linkNode) {
9208
9366
  return;
9209
9367
  }
9210
- const url = this.getInputValue('url');
9368
+ const url = this.container.find('input[name="url"]').value();
9211
9369
  this.writeClipboardText(url).then((error) => {
9212
9370
  const svgNode = this.container.find('button[name="copy"] svg');
9213
9371
  svgNode.hide();
@@ -9253,7 +9411,7 @@ class LinkPopup {
9253
9411
  if (!this.linkNode) {
9254
9412
  return;
9255
9413
  }
9256
- const url = this.getInputValue('url');
9414
+ const url = this.container.find('input[name="url"]').value();
9257
9415
  window.open(url);
9258
9416
  },
9259
9417
  });
@@ -9313,22 +9471,15 @@ class LinkPopup {
9313
9471
  });
9314
9472
  button.render();
9315
9473
  }
9316
- getInputValue(name) {
9317
- const inputElement = this.container.find(`input[name="${name}"]`);
9318
- const nativeInputElement = inputElement.get(0);
9319
- return nativeInputElement.value;
9320
- }
9321
- setInputValue(name, value) {
9322
- const inputElement = this.container.find(`input[name="${name}"]`);
9323
- const nativeInputElement = inputElement.get(0);
9324
- nativeInputElement.value = value;
9474
+ get visible() {
9475
+ return this.container.get(0).isConnected && this.container.computedCSS('display') !== 'none';
9325
9476
  }
9326
9477
  save() {
9327
9478
  if (!this.linkNode) {
9328
9479
  return;
9329
9480
  }
9330
- const url = this.getInputValue('url');
9331
- let title = this.getInputValue('title');
9481
+ const url = this.container.find('input[name="url"]').value();
9482
+ let title = this.container.find('input[name="title"]').value();
9332
9483
  if (url === '' && title === '') {
9333
9484
  this.linkNode.remove();
9334
9485
  return;
@@ -9339,32 +9490,27 @@ class LinkPopup {
9339
9490
  this.linkNode.attr('href', url);
9340
9491
  this.linkNode.text(title);
9341
9492
  }
9342
- updatePosition() {
9493
+ position() {
9343
9494
  if (!this.linkNode) {
9344
9495
  return;
9345
9496
  }
9346
- const position = nodeAndView(this.linkNode);
9497
+ const position = nodePosition(this.linkNode);
9347
9498
  if (position.left < 0 || position.right < 0 || position.top < 0 || position.bottom < 0) {
9348
9499
  this.container.css('visibility', 'hidden');
9349
9500
  return;
9350
9501
  }
9351
9502
  this.container.css('visibility', '');
9352
9503
  const linkNativeNode = this.linkNode.get(0);
9353
- // Returns a DOMRect object providing information about the size of an element and its position relative to the viewport.
9354
9504
  const linkRect = linkNativeNode.getBoundingClientRect();
9355
9505
  const linkX = linkRect.x + window.scrollX;
9356
9506
  const linkY = linkRect.y + window.scrollY;
9357
- // link.x + popup.width > window.width
9358
9507
  if (linkRect.x + this.container.width() > window.innerWidth) {
9359
- // link.x + window.scrollX - (popup.width - link.width)
9360
9508
  this.container.css('left', `${linkX - this.container.width() + linkRect.width}px`);
9361
9509
  }
9362
9510
  else {
9363
9511
  this.container.css('left', `${linkX}px`);
9364
9512
  }
9365
- // link.y + link.height + popup.height > window.height
9366
9513
  if (linkRect.y + linkRect.height + this.container.height() > window.innerHeight) {
9367
- // link.y + window.scrollY - popup.height
9368
9514
  this.container.css('top', `${linkY - this.container.height()}px`);
9369
9515
  }
9370
9516
  else {
@@ -9388,13 +9534,13 @@ class LinkPopup {
9388
9534
  this.linkNode = linkNode;
9389
9535
  const url = linkNode.attr('href');
9390
9536
  const title = linkNode.text();
9391
- this.setInputValue('url', url);
9537
+ this.container.find('input[name="url"]').value(url);
9392
9538
  if (title !== url) {
9393
- this.setInputValue('title', title);
9539
+ this.container.find('input[name="title"]').value(title);
9394
9540
  }
9395
9541
  this.container.css('visibility', 'hidden');
9396
9542
  this.container.show();
9397
- this.updatePosition();
9543
+ this.position();
9398
9544
  this.container.css('visibility', '');
9399
9545
  this.container.find('input[name="url"]').focus();
9400
9546
  }
@@ -9415,31 +9561,38 @@ var link = (editor) => {
9415
9561
  const range = editor.selection.range;
9416
9562
  range.setStartAfter(node);
9417
9563
  range.collapseToStart();
9564
+ editor.selection.sync();
9418
9565
  editor.history.save();
9419
9566
  },
9420
9567
  onRemove: node => {
9421
9568
  const range = editor.selection.range;
9422
9569
  range.setStartAfter(node);
9423
9570
  range.collapseToStart();
9571
+ editor.selection.sync();
9424
9572
  editor.history.save();
9425
9573
  },
9426
9574
  });
9427
- editor.root.on('scroll', () => {
9428
- popup.updatePosition();
9575
+ editor.event.on('scroll', () => {
9576
+ popup.position();
9429
9577
  });
9430
9578
  editor.event.on('resize', () => {
9431
- popup.updatePosition();
9579
+ popup.position();
9432
9580
  });
9433
9581
  editor.event.on('click', (targetNode) => {
9434
- if (targetNode.closest('button[name="link"]').length > 0) {
9582
+ if (popup.container.contains(targetNode)) {
9435
9583
  return;
9436
9584
  }
9437
- const linkNode = targetNode.closest('a');
9438
- if (linkNode.length === 0) {
9439
- popup.hide();
9585
+ if (targetNode.closest('button[name="link"]').length > 0) {
9440
9586
  return;
9441
9587
  }
9442
- if (!editor.container.contains(linkNode) || linkNode.closest('lake-box').length > 0) {
9588
+ const linkNode = targetNode.closest('a');
9589
+ if (linkNode.length === 0 ||
9590
+ !editor.container.contains(linkNode) ||
9591
+ linkNode.closest('lake-box').length > 0) {
9592
+ if (!popup.visible) {
9593
+ return;
9594
+ }
9595
+ editor.selection.sync();
9443
9596
  popup.hide();
9444
9597
  return;
9445
9598
  }
@@ -9621,8 +9774,10 @@ var equation = (editor) => {
9621
9774
  editor.command.add('equation', {
9622
9775
  execute: (value) => {
9623
9776
  const box = editor.selection.insertBox('equation', value);
9624
- editor.selection.selectBox(box);
9625
9777
  editor.history.save();
9778
+ const boxContainer = box.getContainer();
9779
+ boxContainer.addClass('lake-box-activated');
9780
+ boxContainer.find('textarea').focus();
9626
9781
  },
9627
9782
  });
9628
9783
  };
@@ -10194,7 +10349,8 @@ var backspaceKey = (editor) => {
10194
10349
  const boxNode = range.commonAncestor.closest('lake-box');
10195
10350
  const box = getBox(boxNode);
10196
10351
  const boxValue = box.value;
10197
- if (box.name === 'codeBlock' && (boxValue.code === undefined || boxValue.code === '')) {
10352
+ if (range.isCollapsed && box.name === 'codeBlock' &&
10353
+ (boxValue.code === undefined || boxValue.code === '')) {
10198
10354
  event.preventDefault();
10199
10355
  editor.selection.removeBox(box);
10200
10356
  editor.history.save();
@@ -10442,7 +10598,7 @@ var arrowKeys = (editor) => {
10442
10598
  if (editor.readonly) {
10443
10599
  return;
10444
10600
  }
10445
- editor.keystroke.setKeydown('arrow-left', event => {
10601
+ editor.keystroke.setKeydown('left', event => {
10446
10602
  const range = editor.selection.range;
10447
10603
  if (range.isInsideBox) {
10448
10604
  return;
@@ -10482,7 +10638,7 @@ var arrowKeys = (editor) => {
10482
10638
  range.selectBox(prevNode);
10483
10639
  }
10484
10640
  });
10485
- editor.keystroke.setKeydown('arrow-right', event => {
10641
+ editor.keystroke.setKeydown('right', event => {
10486
10642
  const range = editor.selection.range;
10487
10643
  if (range.isInsideBox) {
10488
10644
  return;
@@ -10522,7 +10678,7 @@ var arrowKeys = (editor) => {
10522
10678
  range.selectBox(nextNode);
10523
10679
  }
10524
10680
  });
10525
- editor.keystroke.setKeydown('arrow-up', event => {
10681
+ editor.keystroke.setKeydown('up', event => {
10526
10682
  const range = editor.selection.range;
10527
10683
  if (range.isInsideBox) {
10528
10684
  return;
@@ -10541,7 +10697,7 @@ var arrowKeys = (editor) => {
10541
10697
  range.collapseToStart();
10542
10698
  }
10543
10699
  });
10544
- editor.keystroke.setKeydown('arrow-down', event => {
10700
+ editor.keystroke.setKeydown('down', event => {
10545
10701
  const range = editor.selection.range;
10546
10702
  if (range.isInsideBox) {
10547
10703
  return;
@@ -10583,6 +10739,539 @@ var escapeKey = (editor) => {
10583
10739
  });
10584
10740
  };
10585
10741
 
10742
+ const slashItems = [
10743
+ {
10744
+ name: 'heading1',
10745
+ type: 'button',
10746
+ icon: icons.get('heading1'),
10747
+ title: locale => locale.slash.heading1(),
10748
+ description: locale => locale.slash.heading1Desc(),
10749
+ onClick: editor => {
10750
+ editor.command.execute('heading', 'h1');
10751
+ },
10752
+ },
10753
+ {
10754
+ name: 'heading2',
10755
+ type: 'button',
10756
+ icon: icons.get('heading2'),
10757
+ title: locale => locale.slash.heading2(),
10758
+ description: locale => locale.slash.heading2Desc(),
10759
+ onClick: editor => {
10760
+ editor.command.execute('heading', 'h2');
10761
+ },
10762
+ },
10763
+ {
10764
+ name: 'heading3',
10765
+ type: 'button',
10766
+ icon: icons.get('heading3'),
10767
+ title: locale => locale.slash.heading3(),
10768
+ description: locale => locale.slash.heading3Desc(),
10769
+ onClick: editor => {
10770
+ editor.command.execute('heading', 'h3');
10771
+ },
10772
+ },
10773
+ {
10774
+ name: 'heading4',
10775
+ type: 'button',
10776
+ icon: icons.get('heading4'),
10777
+ title: locale => locale.slash.heading4(),
10778
+ description: locale => locale.slash.heading4Desc(),
10779
+ onClick: editor => {
10780
+ editor.command.execute('heading', 'h4');
10781
+ },
10782
+ },
10783
+ {
10784
+ name: 'heading5',
10785
+ type: 'button',
10786
+ icon: icons.get('heading5'),
10787
+ title: locale => locale.slash.heading5(),
10788
+ description: locale => locale.slash.heading5Desc(),
10789
+ onClick: editor => {
10790
+ editor.command.execute('heading', 'h5');
10791
+ },
10792
+ },
10793
+ {
10794
+ name: 'heading6',
10795
+ type: 'button',
10796
+ icon: icons.get('heading6'),
10797
+ title: locale => locale.slash.heading6(),
10798
+ description: locale => locale.slash.heading6Desc(),
10799
+ onClick: editor => {
10800
+ editor.command.execute('heading', 'h6');
10801
+ },
10802
+ },
10803
+ {
10804
+ name: 'paragraph',
10805
+ type: 'button',
10806
+ icon: icons.get('paragraph'),
10807
+ title: locale => locale.slash.paragraph(),
10808
+ description: locale => locale.slash.paragraphDesc(),
10809
+ onClick: editor => {
10810
+ editor.command.execute('heading', 'p');
10811
+ },
10812
+ },
10813
+ {
10814
+ name: 'blockQuote',
10815
+ type: 'button',
10816
+ icon: icons.get('blockQuote'),
10817
+ title: locale => locale.slash.blockQuote(),
10818
+ description: locale => locale.slash.blockQuoteDesc(),
10819
+ onClick: (editor, value) => {
10820
+ editor.command.execute(value);
10821
+ },
10822
+ },
10823
+ {
10824
+ name: 'numberedList',
10825
+ type: 'button',
10826
+ icon: icons.get('numberedList'),
10827
+ title: locale => locale.slash.numberedList(),
10828
+ description: locale => locale.slash.numberedListDesc(),
10829
+ onClick: editor => {
10830
+ editor.command.execute('list', 'numbered');
10831
+ },
10832
+ },
10833
+ {
10834
+ name: 'bulletedList',
10835
+ type: 'button',
10836
+ icon: icons.get('bulletedList'),
10837
+ title: locale => locale.slash.bulletedList(),
10838
+ description: locale => locale.slash.bulletedListDesc(),
10839
+ onClick: editor => {
10840
+ editor.command.execute('list', 'bulleted');
10841
+ },
10842
+ },
10843
+ {
10844
+ name: 'checklist',
10845
+ type: 'button',
10846
+ icon: icons.get('checklist'),
10847
+ title: locale => locale.slash.checklist(),
10848
+ description: locale => locale.slash.checklistDesc(),
10849
+ onClick: editor => {
10850
+ editor.command.execute('list', 'checklist');
10851
+ },
10852
+ },
10853
+ {
10854
+ name: 'hr',
10855
+ type: 'button',
10856
+ icon: icons.get('hr'),
10857
+ title: locale => locale.slash.hr(),
10858
+ description: locale => locale.slash.hrDesc(),
10859
+ onClick: (editor, value) => {
10860
+ editor.command.execute(value);
10861
+ },
10862
+ },
10863
+ {
10864
+ name: 'codeBlock',
10865
+ type: 'button',
10866
+ icon: icons.get('codeBlock'),
10867
+ title: locale => locale.slash.codeBlock(),
10868
+ description: locale => locale.slash.codeBlockDesc(),
10869
+ onClick: (editor, value) => {
10870
+ editor.command.execute(value);
10871
+ },
10872
+ },
10873
+ {
10874
+ name: 'video',
10875
+ type: 'button',
10876
+ icon: icons.get('video'),
10877
+ title: locale => locale.slash.video(),
10878
+ description: locale => locale.slash.videoDesc(),
10879
+ onClick: (editor, value) => {
10880
+ editor.command.execute(value);
10881
+ },
10882
+ },
10883
+ {
10884
+ name: 'equation',
10885
+ type: 'button',
10886
+ icon: icons.get('equation'),
10887
+ title: locale => locale.slash.equation(),
10888
+ description: locale => locale.slash.equationDesc(),
10889
+ onClick: (editor, value) => {
10890
+ editor.command.execute(value);
10891
+ },
10892
+ },
10893
+ {
10894
+ name: 'image',
10895
+ type: 'upload',
10896
+ icon: icons.get('image'),
10897
+ title: locale => locale.slash.image(),
10898
+ description: locale => locale.slash.imageDesc(),
10899
+ accept: 'image/*',
10900
+ multiple: true,
10901
+ },
10902
+ {
10903
+ name: 'file',
10904
+ type: 'upload',
10905
+ icon: icons.get('attachment'),
10906
+ title: locale => locale.slash.file(),
10907
+ description: locale => locale.slash.fileDesc(),
10908
+ accept: '*',
10909
+ multiple: true,
10910
+ },
10911
+ ];
10912
+
10913
+ const slashItemMap = new Map();
10914
+ for (const item of slashItems) {
10915
+ slashItemMap.set(item.name, item);
10916
+ }
10917
+ class SlashPopup {
10918
+ constructor(config) {
10919
+ this.range = null;
10920
+ this.noMouseEvent = false;
10921
+ this.keyword = null;
10922
+ this.keydownListener = (event) => {
10923
+ if (isKeyHotkey('escape', event)) {
10924
+ event.preventDefault();
10925
+ this.hide();
10926
+ return;
10927
+ }
10928
+ const isDownKey = isKeyHotkey('down', event);
10929
+ const isUpKey = isKeyHotkey('up', event);
10930
+ const isEnterKey = isKeyHotkey('enter', event);
10931
+ if (!isDownKey && !isUpKey && !isEnterKey) {
10932
+ return;
10933
+ }
10934
+ const selectedItemNode = this.container.find('.lake-slash-item-selected');
10935
+ if (selectedItemNode.length === 0) {
10936
+ const firstItem = this.container.find('.lake-slash-item').eq(0);
10937
+ scrollToNode(firstItem, {
10938
+ behavior: 'instant',
10939
+ block: 'start',
10940
+ });
10941
+ firstItem.addClass('lake-slash-item-selected');
10942
+ return;
10943
+ }
10944
+ this.noMouseEvent = true;
10945
+ if (isDownKey) {
10946
+ event.preventDefault();
10947
+ let nextItemNode = selectedItemNode.next();
10948
+ if (nextItemNode.length === 0) {
10949
+ nextItemNode = this.container.find('.lake-slash-item').eq(0);
10950
+ }
10951
+ scrollToNode(nextItemNode, {
10952
+ behavior: 'instant',
10953
+ block: 'end',
10954
+ });
10955
+ this.container.find('.lake-slash-item').removeClass('lake-slash-item-selected');
10956
+ nextItemNode.addClass('lake-slash-item-selected');
10957
+ }
10958
+ else if (isUpKey) {
10959
+ event.preventDefault();
10960
+ let prevItemNode = selectedItemNode.prev();
10961
+ if (prevItemNode.length === 0) {
10962
+ const itemNode = this.container.find('.lake-slash-item');
10963
+ prevItemNode = itemNode.eq(itemNode.length - 1);
10964
+ }
10965
+ scrollToNode(prevItemNode, {
10966
+ behavior: 'instant',
10967
+ block: 'start',
10968
+ });
10969
+ this.container.find('.lake-slash-item').removeClass('lake-slash-item-selected');
10970
+ prevItemNode.addClass('lake-slash-item-selected');
10971
+ }
10972
+ else if (isEnterKey) {
10973
+ event.preventDefault();
10974
+ selectedItemNode.emit('click');
10975
+ }
10976
+ window.setTimeout(() => {
10977
+ this.noMouseEvent = false;
10978
+ }, 50);
10979
+ };
10980
+ this.clickListener = (targetNode) => {
10981
+ if (this.container.contains(targetNode)) {
10982
+ return;
10983
+ }
10984
+ this.hide();
10985
+ };
10986
+ this.scrollListener = () => this.position();
10987
+ this.resizeListener = () => this.position();
10988
+ this.editor = config.editor;
10989
+ this.items = config.items;
10990
+ this.root = config.editor.popupContainer;
10991
+ this.container = query('<ul class="lake-slash-popup" />');
10992
+ }
10993
+ getItem(name) {
10994
+ if (typeof name !== 'string') {
10995
+ return name;
10996
+ }
10997
+ const item = slashItemMap.get(name);
10998
+ if (!item) {
10999
+ throw new Error(`SlashItem "${name}" has not been defined yet.`);
11000
+ }
11001
+ return item;
11002
+ }
11003
+ emptyBlock() {
11004
+ const range = this.editor.selection.range;
11005
+ const block = range.commonAncestor.closestBlock();
11006
+ this.hide();
11007
+ block.empty();
11008
+ appendBreak(block);
11009
+ range.shrinkBefore(block);
11010
+ }
11011
+ appendItem(item) {
11012
+ const editor = this.editor;
11013
+ const itemTitle = typeof item.title === 'string' ? item.title : item.title(editor.locale);
11014
+ const itemDescription = typeof item.description === 'string' ? item.description : item.description(editor.locale);
11015
+ const itemNode = query(safeTemplate `
11016
+ <li class="lake-slash-item" name="${item.name}">
11017
+ <div class="lake-slash-icon"></div>
11018
+ <div class="lake-slash-text">
11019
+ <div class="lake-slash-title">${itemTitle}</div>
11020
+ <div class="lake-slash-description">${itemDescription}</div>
11021
+ </div>
11022
+ </li>
11023
+ `);
11024
+ const icon = item.icon;
11025
+ if (icon) {
11026
+ itemNode.find('.lake-slash-icon').append(icon);
11027
+ }
11028
+ this.container.append(itemNode);
11029
+ itemNode.on('mouseenter', () => {
11030
+ if (this.noMouseEvent) {
11031
+ return;
11032
+ }
11033
+ this.container.find('.lake-slash-item').removeClass('lake-slash-item-selected');
11034
+ itemNode.addClass('lake-slash-item-selected');
11035
+ });
11036
+ itemNode.on('mouseleave', () => {
11037
+ if (this.noMouseEvent) {
11038
+ return;
11039
+ }
11040
+ itemNode.removeClass('lake-slash-item-selected');
11041
+ });
11042
+ if (item.type === 'upload') {
11043
+ itemNode.append('<input type="file" />');
11044
+ const fileNode = itemNode.find('input[type="file"]');
11045
+ const fileNativeNode = fileNode.get(0);
11046
+ if (item.accept) {
11047
+ fileNode.attr('accept', item.accept);
11048
+ }
11049
+ if (item.multiple === true) {
11050
+ fileNode.attr('multiple', 'true');
11051
+ }
11052
+ fileNode.on('click', event => event.stopPropagation());
11053
+ fileNode.on('change', event => {
11054
+ editor.focus();
11055
+ this.emptyBlock();
11056
+ const target = event.target;
11057
+ const files = target.files || [];
11058
+ for (const file of files) {
11059
+ uploadFile({
11060
+ editor,
11061
+ name: item.name,
11062
+ file,
11063
+ onError: error => editor.config.onMessage('error', error),
11064
+ });
11065
+ }
11066
+ });
11067
+ itemNode.on('click', () => {
11068
+ fileNativeNode.click();
11069
+ });
11070
+ }
11071
+ else {
11072
+ itemNode.on('click', () => {
11073
+ editor.focus();
11074
+ this.emptyBlock();
11075
+ item.onClick(editor, item.name);
11076
+ });
11077
+ }
11078
+ }
11079
+ get visible() {
11080
+ return this.container.get(0).isConnected && this.container.computedCSS('display') !== 'none';
11081
+ }
11082
+ search(keyword) {
11083
+ const editor = this.editor;
11084
+ const localeEnglish = i18nObject('en-US');
11085
+ keyword = keyword.toLowerCase();
11086
+ const items = [];
11087
+ for (const name of this.items) {
11088
+ const item = this.getItem(name);
11089
+ let itemTitle = typeof item.title === 'string' ? item.title : item.title(editor.locale);
11090
+ itemTitle = itemTitle.toLowerCase();
11091
+ let itemTitleEnglish = typeof item.title === 'string' ? item.title : item.title(localeEnglish);
11092
+ itemTitleEnglish = itemTitleEnglish.toLowerCase();
11093
+ if (itemTitle.indexOf(keyword) >= 0 ||
11094
+ itemTitle.replace(/\s+/g, '').indexOf(keyword) >= 0 ||
11095
+ itemTitleEnglish.indexOf(keyword) >= 0 ||
11096
+ itemTitleEnglish.replace(/\s+/g, '').indexOf(keyword) >= 0) {
11097
+ items.push(typeof name === 'string' ? item.name : name);
11098
+ }
11099
+ }
11100
+ return items;
11101
+ }
11102
+ position() {
11103
+ if (!this.range) {
11104
+ return;
11105
+ }
11106
+ this.container.css('visibility', '');
11107
+ const rangeRect = this.range.get().getBoundingClientRect();
11108
+ const rangeX = rangeRect.x + window.scrollX;
11109
+ const rangeY = rangeRect.y + window.scrollY;
11110
+ if (rangeRect.x + this.container.width() > window.innerWidth) {
11111
+ this.container.css('left', `${rangeX - this.container.width() + rangeRect.width}px`);
11112
+ }
11113
+ else {
11114
+ this.container.css('left', `${rangeX}px`);
11115
+ }
11116
+ if (rangeRect.y + rangeRect.height + this.container.height() > window.innerHeight) {
11117
+ this.container.css('top', `${rangeY - this.container.height() - 5}px`);
11118
+ }
11119
+ else {
11120
+ this.container.css('top', `${rangeY + rangeRect.height + 5}px`);
11121
+ }
11122
+ }
11123
+ render() {
11124
+ this.root.append(this.container);
11125
+ this.update();
11126
+ }
11127
+ update(keyword = null) {
11128
+ if (keyword !== null && this.keyword === keyword) {
11129
+ return;
11130
+ }
11131
+ const items = keyword !== null ? this.search(keyword) : this.items;
11132
+ if (items.length === 0) {
11133
+ this.hide();
11134
+ return;
11135
+ }
11136
+ this.keyword = keyword;
11137
+ this.container.empty();
11138
+ for (const name of items) {
11139
+ const item = this.getItem(name);
11140
+ this.appendItem(item);
11141
+ }
11142
+ const selectedItemNode = this.container.find('.lake-slash-item-selected');
11143
+ if (selectedItemNode.length === 0) {
11144
+ this.container.find('.lake-slash-item').eq(0).addClass('lake-slash-item-selected');
11145
+ }
11146
+ this.position();
11147
+ }
11148
+ show(range, keyword) {
11149
+ const editor = this.editor;
11150
+ if (this.root.find('.lake-slash-popup').length === 0) {
11151
+ this.render();
11152
+ }
11153
+ else {
11154
+ this.update();
11155
+ }
11156
+ this.range = range;
11157
+ this.container.css('visibility', 'hidden');
11158
+ this.container.show();
11159
+ this.position();
11160
+ // for fixing the container's width
11161
+ this.container.css('width', '');
11162
+ this.container.css('width', `${this.container.width()}px`);
11163
+ if (keyword) {
11164
+ this.update(keyword);
11165
+ }
11166
+ this.container.css('visibility', '');
11167
+ document.addEventListener('keydown', this.keydownListener, true);
11168
+ editor.event.on('click', this.clickListener);
11169
+ editor.event.on('scroll', this.scrollListener);
11170
+ editor.event.on('resize', this.resizeListener);
11171
+ }
11172
+ hide() {
11173
+ const editor = this.editor;
11174
+ this.range = null;
11175
+ this.container.hide();
11176
+ document.removeEventListener('keydown', this.keydownListener, true);
11177
+ editor.event.off('click', this.clickListener);
11178
+ editor.event.off('scroll', this.scrollListener);
11179
+ editor.event.off('resize', this.resizeListener);
11180
+ }
11181
+ unmount() {
11182
+ this.hide();
11183
+ this.container.remove();
11184
+ }
11185
+ }
11186
+
11187
+ const defaultItems = [
11188
+ 'heading1',
11189
+ 'heading2',
11190
+ 'heading3',
11191
+ 'heading4',
11192
+ 'heading5',
11193
+ 'heading6',
11194
+ 'paragraph',
11195
+ 'blockQuote',
11196
+ 'numberedList',
11197
+ 'bulletedList',
11198
+ 'checklist',
11199
+ 'hr',
11200
+ ];
11201
+ function getKeyword(block) {
11202
+ let text = block.text().trim();
11203
+ text = text.replace(/[\u200B\u2060]/g, '');
11204
+ if (!/^\//.test(text)) {
11205
+ return null;
11206
+ }
11207
+ return text.substring(1);
11208
+ }
11209
+ function showPopup(editor, popup) {
11210
+ const range = editor.selection.range;
11211
+ if (!range.isCollapsed) {
11212
+ return;
11213
+ }
11214
+ const block = range.getBlocks()[0];
11215
+ if (!block) {
11216
+ return;
11217
+ }
11218
+ if (block.find('lake-box').length > 0) {
11219
+ return;
11220
+ }
11221
+ const keyword = getKeyword(block);
11222
+ if (keyword === null) {
11223
+ return;
11224
+ }
11225
+ const slashRange = range.clone();
11226
+ slashRange.selectNodeContents(block);
11227
+ popup.show(slashRange, keyword);
11228
+ }
11229
+ var slash = (editor) => {
11230
+ editor.setPluginConfig('slash', {
11231
+ items: defaultItems,
11232
+ });
11233
+ if (editor.readonly) {
11234
+ return;
11235
+ }
11236
+ const popup = new SlashPopup({
11237
+ editor,
11238
+ items: editor.config.slash.items,
11239
+ });
11240
+ editor.container.on('keyup', event => {
11241
+ if (editor.isComposing) {
11242
+ return;
11243
+ }
11244
+ const keyboardEvent = event;
11245
+ if (isKeyHotkey(['down', 'up', 'enter'], keyboardEvent)) {
11246
+ return;
11247
+ }
11248
+ if (!popup.visible) {
11249
+ if (isKeyHotkey('/', keyboardEvent)) {
11250
+ showPopup(editor, popup);
11251
+ return;
11252
+ }
11253
+ if (isKeyHotkey(['backspace', 'delete'], keyboardEvent)) {
11254
+ showPopup(editor, popup);
11255
+ }
11256
+ else {
11257
+ return;
11258
+ }
11259
+ }
11260
+ const range = editor.selection.range;
11261
+ const block = range.getBlocks()[0];
11262
+ if (!block) {
11263
+ return;
11264
+ }
11265
+ const keyword = getKeyword(block);
11266
+ if (keyword === null) {
11267
+ popup.hide();
11268
+ return;
11269
+ }
11270
+ popup.update(keyword);
11271
+ });
11272
+ return () => popup.unmount();
11273
+ };
11274
+
10586
11275
  Editor.box.add(hrBox);
10587
11276
  Editor.box.add(codeBlockBox);
10588
11277
  Editor.box.add(imageBox);
@@ -10590,48 +11279,49 @@ Editor.box.add(videoBox);
10590
11279
  Editor.box.add(fileBox);
10591
11280
  Editor.box.add(emojiBox);
10592
11281
  Editor.box.add(equationBox);
10593
- Editor.plugin.add(copy);
10594
- Editor.plugin.add(cut);
10595
- Editor.plugin.add(paste);
10596
- Editor.plugin.add(drop);
10597
- Editor.plugin.add(undo);
10598
- Editor.plugin.add(redo);
10599
- Editor.plugin.add(selectAll);
10600
- Editor.plugin.add(heading);
10601
- Editor.plugin.add(blockQuote);
10602
- Editor.plugin.add(list);
10603
- Editor.plugin.add(align);
10604
- Editor.plugin.add(indent);
10605
- Editor.plugin.add(bold);
10606
- Editor.plugin.add(italic);
10607
- Editor.plugin.add(underline);
10608
- Editor.plugin.add(strikethrough);
10609
- Editor.plugin.add(subscript);
10610
- Editor.plugin.add(superscript);
10611
- Editor.plugin.add(code);
10612
- Editor.plugin.add(fontFamily);
10613
- Editor.plugin.add(fontSize);
10614
- Editor.plugin.add(fontColor);
10615
- Editor.plugin.add(highlight);
10616
- Editor.plugin.add(removeFormat);
10617
- Editor.plugin.add(formatPainter);
10618
- Editor.plugin.add(link);
10619
- Editor.plugin.add(hr);
10620
- Editor.plugin.add(codeBlock);
10621
- Editor.plugin.add(image);
10622
- Editor.plugin.add(video);
10623
- Editor.plugin.add(file);
10624
- Editor.plugin.add(emoji);
10625
- Editor.plugin.add(equation);
10626
- Editor.plugin.add(specialCharacter);
10627
- Editor.plugin.add(markdown);
10628
- Editor.plugin.add(enterKey);
10629
- Editor.plugin.add(shiftEnterKey);
10630
- Editor.plugin.add(backspaceKey);
10631
- Editor.plugin.add(deleteKey);
10632
- Editor.plugin.add(tabKey);
10633
- Editor.plugin.add(arrowKeys);
10634
- Editor.plugin.add(escapeKey);
11282
+ Editor.plugin.add('copy', copy);
11283
+ Editor.plugin.add('cut', cut);
11284
+ Editor.plugin.add('paste', paste);
11285
+ Editor.plugin.add('drop', drop);
11286
+ Editor.plugin.add('undo', undo);
11287
+ Editor.plugin.add('redo', redo);
11288
+ Editor.plugin.add('selectAll', selectAll);
11289
+ Editor.plugin.add('heading', heading);
11290
+ Editor.plugin.add('blockQuote', blockQuote);
11291
+ Editor.plugin.add('list', list);
11292
+ Editor.plugin.add('align', align);
11293
+ Editor.plugin.add('indent', indent);
11294
+ Editor.plugin.add('bold', bold);
11295
+ Editor.plugin.add('italic', italic);
11296
+ Editor.plugin.add('underline', underline);
11297
+ Editor.plugin.add('strikethrough', strikethrough);
11298
+ Editor.plugin.add('subscript', subscript);
11299
+ Editor.plugin.add('superscript', superscript);
11300
+ Editor.plugin.add('code', code);
11301
+ Editor.plugin.add('fontFamily', fontFamily);
11302
+ Editor.plugin.add('fontSize', fontSize);
11303
+ Editor.plugin.add('fontColor', fontColor);
11304
+ Editor.plugin.add('highlight', highlight);
11305
+ Editor.plugin.add('removeFormat', removeFormat);
11306
+ Editor.plugin.add('formatPainter', formatPainter);
11307
+ Editor.plugin.add('link', link);
11308
+ Editor.plugin.add('hr', hr);
11309
+ Editor.plugin.add('codeBlock', codeBlock);
11310
+ Editor.plugin.add('image', image);
11311
+ Editor.plugin.add('video', video);
11312
+ Editor.plugin.add('file', file);
11313
+ Editor.plugin.add('emoji', emoji);
11314
+ Editor.plugin.add('equation', equation);
11315
+ Editor.plugin.add('specialCharacter', specialCharacter);
11316
+ Editor.plugin.add('markdown', markdown);
11317
+ Editor.plugin.add('enterKey', enterKey);
11318
+ Editor.plugin.add('shiftEnterKey', shiftEnterKey);
11319
+ Editor.plugin.add('backspaceKey', backspaceKey);
11320
+ Editor.plugin.add('deleteKey', deleteKey);
11321
+ Editor.plugin.add('tabKey', tabKey);
11322
+ Editor.plugin.add('arrowKeys', arrowKeys);
11323
+ Editor.plugin.add('escapeKey', escapeKey);
11324
+ Editor.plugin.add('slash', slash);
10635
11325
 
10636
11326
  export { Box, Button, Dropdown, Editor, Fragment, HTMLParser, Nodes, Range, TextParser, Toolbar, index as Utils, addMark, deleteContents, fixList, icons, insertBookmark, insertBox, insertFragment, insertLink, insertNode, removeBox, removeMark, setBlocks, splitBlock$1 as splitBlock, splitMarks, toBookmark };
10637
11327
  //# sourceMappingURL=lake.js.map