lakelib 0.1.14 → 0.1.16

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/lib/lake.js CHANGED
@@ -1,9 +1,7 @@
1
- import { Base64 } from 'js-base64';
2
1
  import EventEmitter from 'eventemitter3';
3
2
  import { i18nObject as i18nObject$1 } from 'typesafe-i18n';
4
- import debounce from 'lodash/debounce';
5
- import isEqual from 'lodash/isEqual';
6
- import md5 from 'blueimp-md5';
3
+ import debounce from 'debounce';
4
+ import isEqual from 'fast-deep-equal/es6';
7
5
  import { createKeybindingsHandler } from 'tinykeys';
8
6
  import 'photoswipe/style.css';
9
7
  import PhotoSwipeLightbox from 'photoswipe/lightbox';
@@ -135,17 +133,21 @@ var unlink = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32
135
133
 
136
134
  var hr$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128Z\"></path></svg>";
137
135
 
136
+ var codeBlock$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M58.34,101.66l-32-32a8,8,0,0,1,0-11.32l32-32A8,8,0,0,1,69.66,37.66L43.31,64,69.66,90.34a8,8,0,0,1-11.32,11.32Zm40,0a8,8,0,0,0,11.32,0l32-32a8,8,0,0,0,0-11.32l-32-32A8,8,0,0,0,98.34,37.66L124.69,64,98.34,90.34A8,8,0,0,0,98.34,101.66ZM200,40H176a8,8,0,0,0,0,16h24V200H56V136a8,8,0,0,0-16,0v64a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Z\"></path></svg>";
137
+
138
138
  var image$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,16V158.75l-26.07-26.06a16,16,0,0,0-22.63,0l-20,20-44-44a16,16,0,0,0-22.62,0L40,149.37V56ZM40,172l52-52,80,80H40Zm176,28H194.63l-36-36,20-20L216,181.38V200ZM144,100a12,12,0,1,1,12,12A12,12,0,0,1,144,100Z\"></path></svg>";
139
139
 
140
140
  var video$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M164.44,121.34l-48-32A8,8,0,0,0,104,96v64a8,8,0,0,0,12.44,6.66l48-32a8,8,0,0,0,0-13.32ZM120,145.05V111l25.58,17ZM234.33,69.52a24,24,0,0,0-14.49-16.4C185.56,39.88,131,40,128,40s-57.56-.12-91.84,13.12a24,24,0,0,0-14.49,16.4C19.08,79.5,16,97.74,16,128s3.08,48.5,5.67,58.48a24,24,0,0,0,14.49,16.41C69,215.56,120.4,216,127.34,216h1.32c6.94,0,58.37-.44,91.18-13.11a24,24,0,0,0,14.49-16.41c2.59-10,5.67-28.22,5.67-58.48S236.92,79.5,234.33,69.52Zm-15.49,113a8,8,0,0,1-4.77,5.49c-31.65,12.22-85.48,12-86,12H128c-.54,0-54.33.2-86-12a8,8,0,0,1-4.77-5.49C34.8,173.39,32,156.57,32,128s2.8-45.39,5.16-54.47A8,8,0,0,1,41.93,68c30.52-11.79,81.66-12,85.85-12h.27c.54,0,54.38-.18,86,12a8,8,0,0,1,4.77,5.49C221.2,82.61,224,99.43,224,128S221.2,173.39,218.84,182.47Z\"></path></svg>";
141
141
 
142
142
  var attachment = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M209.66,122.34a8,8,0,0,1,0,11.32l-82.05,82a56,56,0,0,1-79.2-79.21L147.67,35.73a40,40,0,1,1,56.61,56.55L105,193A24,24,0,1,1,71,159L154.3,74.38A8,8,0,1,1,165.7,85.6L82.39,170.31a8,8,0,1,0,11.27,11.36L192.93,81A24,24,0,1,0,159,47L59.76,147.68a40,40,0,1,0,56.53,56.62l82.06-82A8,8,0,0,1,209.66,122.34Z\"></path></svg>";
143
143
 
144
- var codeBlock$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M58.34,101.66l-32-32a8,8,0,0,1,0-11.32l32-32A8,8,0,0,1,69.66,37.66L43.31,64,69.66,90.34a8,8,0,0,1-11.32,11.32Zm40,0a8,8,0,0,0,11.32,0l32-32a8,8,0,0,0,0-11.32l-32-32A8,8,0,0,0,98.34,37.66L124.69,64,98.34,90.34A8,8,0,0,0,98.34,101.66ZM200,40H176a8,8,0,0,0,0,16h24V200H56V136a8,8,0,0,0-16,0v64a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Z\"></path></svg>";
144
+ var emoji$1 = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216ZM80,108a12,12,0,1,1,12,12A12,12,0,0,1,80,108Zm96,0a12,12,0,1,1-12-12A12,12,0,0,1,176,108Zm-1.07,48c-10.29,17.79-27.4,28-46.93,28s-36.63-10.2-46.92-28a8,8,0,1,1,13.84-8c7.47,12.91,19.21,20,33.08,20s25.61-7.1,33.07-20a8,8,0,0,1,13.86,8Z\"></path></svg>";
145
+
146
+ var specialCharacter = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 32 32\"><path d=\"M22.7373,25A14.3093,14.3093,0,0,0,27,15C27,8.42,22.58,4,16,4S5,8.42,5,15A14.3093,14.3093,0,0,0,9.2627,25H4v2h8V24.7617A12.5683,12.5683,0,0,1,7,15c0-5.4673,3.5327-9,9-9s9,3.5327,9,9a12.5683,12.5683,0,0,1-5,9.7617V27h8V25Z\"/></svg>";
145
147
 
146
148
  var table = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"#000000\" viewBox=\"0 0 256 256\"><path d=\"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM40,112H80v32H40Zm56,0H216v32H96ZM216,64V96H40V64ZM40,160H80v32H40Zm176,32H96V160H216v32Z\"></path></svg>";
147
149
 
148
- // These icons are from open source projects.
150
+ // These icons are sourced from open source projects.
149
151
  //
150
152
  // Fluent Icons by Microsoft (https://fluenticons.co/).
151
153
  // - superscript.svg
@@ -217,10 +219,12 @@ const icons = new Map([
217
219
  ['link', link$1],
218
220
  ['unlink', unlink],
219
221
  ['hr', hr$1],
222
+ ['codeBlock', codeBlock$1],
220
223
  ['image', image$1],
221
224
  ['video', video$1],
222
225
  ['attachment', attachment],
223
- ['codeBlock', codeBlock$1],
226
+ ['emoji', emoji$1],
227
+ ['specialCharacter', specialCharacter],
224
228
  ['table', table],
225
229
  ]);
226
230
 
@@ -269,9 +273,10 @@ function safeTemplate(strings, ...keys) {
269
273
  function camelCase(value) {
270
274
  const valueList = value.split('-');
271
275
  let camelString = '';
272
- valueList.forEach((val, index) => {
273
- camelString += (index > 0) ? val.charAt(0).toUpperCase() + val.substring(1) : val;
274
- });
276
+ for (let i = 0; i < valueList.length; i++) {
277
+ const val = valueList[i];
278
+ camelString += (i > 0) ? val.charAt(0).toUpperCase() + val.substring(1) : val;
279
+ }
275
280
  return camelString;
276
281
  }
277
282
 
@@ -283,6 +288,28 @@ function inString(string, value, delimiter) {
283
288
  return (delimiter + string + delimiter).indexOf(delimiter + value + delimiter) >= 0;
284
289
  }
285
290
 
291
+ // Creates a Base64-encoded ASCII string from a string.
292
+ function toBase64(value) {
293
+ const encoder = new TextEncoder();
294
+ const byteArray = encoder.encode(value);
295
+ let binaryString = '';
296
+ byteArray.forEach(byte => {
297
+ binaryString += String.fromCharCode(byte);
298
+ });
299
+ return window.btoa(binaryString);
300
+ }
301
+
302
+ // Decodes a string of data which has been encoded using Base64 encoding.
303
+ function fromBase64(value) {
304
+ const binaryString = window.atob(value);
305
+ const byteArray = new Uint8Array(binaryString.length);
306
+ for (let i = 0; i < binaryString.length; i++) {
307
+ byteArray[i] = binaryString.charCodeAt(i);
308
+ }
309
+ const decoder = new TextDecoder();
310
+ return decoder.decode(byteArray);
311
+ }
312
+
286
313
  // Converts an alpha value to a hex value.
287
314
  function alphaToHex(value) {
288
315
  const hexString = Math.round(Number.parseFloat(value) * 255).toString(16);
@@ -876,15 +903,16 @@ class Nodes {
876
903
  var _a;
877
904
  const elementId = element.lakeId;
878
905
  const eventItems = (_a = eventData[elementId]) !== null && _a !== void 0 ? _a : [];
879
- eventItems.forEach((item, index) => {
906
+ for (let i = 0; i < eventItems.length; i++) {
907
+ const item = eventItems[i];
880
908
  if (!type || type === item.type && (!listener || listener === item.listener)) {
881
909
  element.removeEventListener(item.type, item.listener, false);
882
- eventItems[index] = {
910
+ eventItems[i] = {
883
911
  type: '',
884
912
  listener: () => { },
885
913
  };
886
914
  }
887
- });
915
+ }
888
916
  eventData[elementId] = eventItems.filter((item) => item.type !== '');
889
917
  });
890
918
  }
@@ -893,11 +921,11 @@ class Nodes {
893
921
  return this.eachElement(element => {
894
922
  const elementId = element.lakeId;
895
923
  const eventItems = eventData[elementId];
896
- eventItems.forEach(item => {
924
+ for (const item of eventItems) {
897
925
  if (item.type === type) {
898
926
  item.listener(event !== null && event !== void 0 ? event : new Event(type));
899
927
  }
900
- });
928
+ }
901
929
  });
902
930
  }
903
931
  // Gets all event listeners attached to the Nodes object.
@@ -953,9 +981,9 @@ class Nodes {
953
981
  }
954
982
  addClass(className) {
955
983
  if (Array.isArray(className)) {
956
- className.forEach(name => {
984
+ for (const name of className) {
957
985
  this.addClass(name);
958
- });
986
+ }
959
987
  return this;
960
988
  }
961
989
  return this.eachElement(element => {
@@ -967,9 +995,9 @@ class Nodes {
967
995
  }
968
996
  removeClass(className) {
969
997
  if (Array.isArray(className)) {
970
- className.forEach(name => {
998
+ for (const name of className) {
971
999
  this.removeClass(name);
972
- });
1000
+ }
973
1001
  return this;
974
1002
  }
975
1003
  return this.eachElement(element => {
@@ -1061,22 +1089,21 @@ class Nodes {
1061
1089
  this.html('');
1062
1090
  return this;
1063
1091
  }
1064
- // Inserts the specified content as the first child of each element.
1092
+ // Inserts the specified content to the beginning of the first element.
1065
1093
  prepend(content) {
1094
+ const element = this.get(0);
1066
1095
  if (typeof content === 'string') {
1067
- return this.eachElement(element => {
1068
- const list = toNodeList(content).reverse();
1069
- list.forEach((node) => {
1070
- if (element.firstChild) {
1071
- element.insertBefore(node, element.firstChild);
1072
- }
1073
- else {
1074
- element.appendChild(node);
1075
- }
1076
- });
1077
- });
1096
+ const list = toNodeList(content).reverse();
1097
+ for (const node of list) {
1098
+ if (element.firstChild) {
1099
+ element.insertBefore(node, element.firstChild);
1100
+ }
1101
+ else {
1102
+ element.appendChild(node);
1103
+ }
1104
+ }
1105
+ return this;
1078
1106
  }
1079
- const element = this.get(0);
1080
1107
  if (content instanceof Nodes) {
1081
1108
  content = content.get(0);
1082
1109
  }
@@ -1088,71 +1115,62 @@ class Nodes {
1088
1115
  }
1089
1116
  return this;
1090
1117
  }
1091
- // Inserts the specified content as the last child of each element.
1118
+ // Inserts the specified content to the end of the first element.
1092
1119
  append(content) {
1120
+ const element = this.get(0);
1093
1121
  if (typeof content === 'string') {
1094
- return this.eachElement(element => {
1095
- const list = toNodeList(content);
1096
- list.forEach((node) => {
1097
- element.appendChild(node);
1098
- });
1099
- });
1122
+ const list = toNodeList(content);
1123
+ for (const node of list) {
1124
+ element.appendChild(node);
1125
+ }
1126
+ return this;
1100
1127
  }
1101
- const element = this.get(0);
1102
1128
  if (content instanceof Nodes) {
1103
1129
  content = content.get(0);
1104
1130
  }
1105
1131
  element.appendChild(content);
1106
1132
  return this;
1107
1133
  }
1108
- // Inserts the specified content before each node.
1134
+ // Inserts the specified content before the first node.
1109
1135
  before(content) {
1136
+ const node = this.get(0);
1137
+ if (!node.parentNode) {
1138
+ return this;
1139
+ }
1110
1140
  if (typeof content === 'string') {
1111
- return this.each(node => {
1112
- const list = toNodeList(content);
1113
- list.forEach(target => {
1114
- if (!node.parentNode) {
1115
- return;
1116
- }
1117
- node.parentNode.insertBefore(target, node);
1118
- });
1119
- });
1141
+ const list = toNodeList(content);
1142
+ for (const target of list) {
1143
+ node.parentNode.insertBefore(target, node);
1144
+ }
1145
+ return this;
1120
1146
  }
1121
- const node = this.get(0);
1122
1147
  if (content instanceof Nodes) {
1123
1148
  content = content.get(0);
1124
1149
  }
1125
- if (!node.parentNode) {
1126
- return this;
1127
- }
1128
1150
  node.parentNode.insertBefore(content, node);
1129
1151
  return this;
1130
1152
  }
1131
- // Inserts the specified content after each node.
1153
+ // Inserts the specified content after the first node.
1132
1154
  after(content) {
1155
+ const node = this.get(0);
1156
+ if (!node.parentNode) {
1157
+ return this;
1158
+ }
1133
1159
  if (typeof content === 'string') {
1134
- return this.each(node => {
1135
- const list = toNodeList(content).reverse();
1136
- list.forEach(target => {
1137
- if (!node.parentNode) {
1138
- return;
1139
- }
1140
- if (node.nextSibling) {
1141
- node.parentNode.insertBefore(target, node.nextSibling);
1142
- }
1143
- else {
1144
- node.parentNode.appendChild(target);
1145
- }
1146
- });
1147
- });
1160
+ const list = toNodeList(content).reverse();
1161
+ for (const target of list) {
1162
+ if (node.nextSibling) {
1163
+ node.parentNode.insertBefore(target, node.nextSibling);
1164
+ }
1165
+ else {
1166
+ node.parentNode.appendChild(target);
1167
+ }
1168
+ }
1169
+ return this;
1148
1170
  }
1149
- const node = this.get(0);
1150
1171
  if (content instanceof Nodes) {
1151
1172
  content = content.get(0);
1152
1173
  }
1153
- if (!node.parentNode) {
1154
- return this;
1155
- }
1156
1174
  if (node.nextSibling) {
1157
1175
  node.parentNode.insertBefore(content, node.nextSibling);
1158
1176
  }
@@ -1161,21 +1179,21 @@ class Nodes {
1161
1179
  }
1162
1180
  return this;
1163
1181
  }
1164
- // Replaces each node with the provided new content.
1182
+ // Replaces the first node with the provided new content.
1165
1183
  replaceWith(newContent) {
1166
- return this.each(node => {
1167
- let target;
1168
- if (newContent instanceof Nodes) {
1169
- target = newContent.get(0);
1170
- }
1171
- else {
1172
- target = toNodeList(newContent)[0];
1173
- }
1174
- if (!node.parentNode) {
1175
- return;
1176
- }
1177
- node.parentNode.replaceChild(target, node);
1178
- });
1184
+ const node = this.get(0);
1185
+ if (!node.parentNode) {
1186
+ return this;
1187
+ }
1188
+ let target;
1189
+ if (newContent instanceof Nodes) {
1190
+ target = newContent.get(0);
1191
+ }
1192
+ else {
1193
+ target = toNodeList(newContent)[0];
1194
+ }
1195
+ node.parentNode.replaceChild(target, node);
1196
+ return this;
1179
1197
  }
1180
1198
  // Removes each node from the DOM.
1181
1199
  // keepChildren parameter:
@@ -1470,7 +1488,7 @@ class Range {
1470
1488
  selectBox(boxNode) {
1471
1489
  const boxContainer = boxNode.find('.lake-box-container');
1472
1490
  if (boxContainer.length === 0) {
1473
- throw new Error(`The box cannot be selected because the box '${boxNode.attr('name')}' (id=${boxNode.id}) has not been rendered yet.`);
1491
+ throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1474
1492
  }
1475
1493
  this.setStart(boxContainer, 0);
1476
1494
  this.collapseToStart();
@@ -1479,7 +1497,7 @@ class Range {
1479
1497
  selectBoxStart(boxNode) {
1480
1498
  const boxStrip = boxNode.find('.lake-box-strip');
1481
1499
  if (boxStrip.length === 0) {
1482
- throw new Error(`The box cannot be selected because the box '${boxNode.attr('name')}' (id=${boxNode.id}) has not been rendered yet.`);
1500
+ throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1483
1501
  }
1484
1502
  this.selectNodeContents(boxStrip.eq(0));
1485
1503
  this.collapseToStart();
@@ -1488,7 +1506,7 @@ class Range {
1488
1506
  selectBoxEnd(boxNode) {
1489
1507
  const boxStrip = boxNode.find('.lake-box-strip');
1490
1508
  if (boxStrip.length === 0) {
1491
- throw new Error(`The box cannot be selected because the box '${boxNode.attr('name')}' (id=${boxNode.id}) has not been rendered yet.`);
1509
+ throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1492
1510
  }
1493
1511
  this.selectNodeContents(boxStrip.eq(1));
1494
1512
  this.collapseToStart();
@@ -2020,9 +2038,9 @@ function wrapNodeList(nodeList, wrapper) {
2020
2038
  wrapper = wrapper.clone(true);
2021
2039
  const deepestElement = getDeepest(wrapper);
2022
2040
  nodeList[0].before(wrapper);
2023
- nodeList.forEach(node => {
2041
+ for (const node of nodeList) {
2024
2042
  deepestElement.append(node);
2025
- });
2043
+ }
2026
2044
  return wrapper;
2027
2045
  }
2028
2046
 
@@ -2855,6 +2873,7 @@ var enUS = {
2855
2873
  highlight: 'Highlight',
2856
2874
  image: 'Image',
2857
2875
  file: 'File',
2876
+ emoji: 'Emoji',
2858
2877
  removeColor: 'Remove color',
2859
2878
  },
2860
2879
  link: {
@@ -2938,6 +2957,7 @@ var zhCN = {
2938
2957
  highlight: '文字背景',
2939
2958
  image: '图片',
2940
2959
  file: '文件',
2960
+ emoji: '表情',
2941
2961
  removeColor: '默认',
2942
2962
  },
2943
2963
  link: {
@@ -3021,6 +3041,7 @@ var ja = {
3021
3041
  highlight: '文字の背景',
3022
3042
  image: '画像',
3023
3043
  file: 'ファイル',
3044
+ emoji: '絵文字',
3024
3045
  removeColor: 'デフォルト',
3025
3046
  },
3026
3047
  link: {
@@ -3104,6 +3125,7 @@ var ko = {
3104
3125
  highlight: '글자 배경',
3105
3126
  image: '이미지',
3106
3127
  file: '파일',
3128
+ emoji: '이모지',
3107
3129
  removeColor: '기본색',
3108
3130
  },
3109
3131
  link: {
@@ -3205,11 +3227,11 @@ class Dropdown {
3205
3227
  if (value === '') {
3206
3228
  return [];
3207
3229
  }
3208
- return JSON.parse(Base64.decode(value));
3230
+ return JSON.parse(fromBase64(value));
3209
3231
  }
3210
3232
  // Updates the value of the node.
3211
3233
  static setValue(node, value) {
3212
- node.attr('value', Base64.encode(JSON.stringify(value)));
3234
+ node.attr('value', toBase64(JSON.stringify(value)));
3213
3235
  }
3214
3236
  static getMenuMap(menuItems, locale) {
3215
3237
  const menuMap = new Map();
@@ -3242,7 +3264,11 @@ class Dropdown {
3242
3264
  `;
3243
3265
  const listNode = query(listContent);
3244
3266
  menuNode.append(listNode);
3245
- if (config.menuType === 'color') {
3267
+ if (config.menuType === 'character') {
3268
+ listNode.attr('title', menuText);
3269
+ listNode.find('.lake-dropdown-menu-text').text(menuItem.value);
3270
+ }
3271
+ else if (config.menuType === 'color') {
3246
3272
  listNode.attr('title', menuText);
3247
3273
  listNode.find('.lake-dropdown-menu-text').css('background-color', menuItem.value);
3248
3274
  }
@@ -3284,7 +3310,7 @@ class Dropdown {
3284
3310
  }
3285
3311
  });
3286
3312
  menuNode.css('visibility', 'hidden');
3287
- menuNode.show(config.menuType === 'color' ? 'flex' : 'block');
3313
+ menuNode.show(config.menuType === 'list' ? 'block' : 'flex');
3288
3314
  const dropdownNativeNode = dropdownNode.get(0);
3289
3315
  const dropdownRect = dropdownNativeNode.getBoundingClientRect();
3290
3316
  // A overflow width on the left side, greater than 0 indicates an overflow.
@@ -3347,7 +3373,7 @@ class Dropdown {
3347
3373
  if (dropdownNode.attr('disabled')) {
3348
3374
  return;
3349
3375
  }
3350
- const value = dropdownNode.attr('color') || config.defaultValue;
3376
+ const value = dropdownNode.attr('color') || config.defaultValue || '';
3351
3377
  config.onSelect(value);
3352
3378
  });
3353
3379
  }
@@ -3358,7 +3384,11 @@ class Dropdown {
3358
3384
  });
3359
3385
  menuNode.on('click', event => {
3360
3386
  event.preventDefault();
3387
+ event.stopPropagation();
3361
3388
  const listItem = query(event.target).closest('li');
3389
+ if (listItem.length === 0) {
3390
+ return;
3391
+ }
3362
3392
  const value = listItem.attr('value');
3363
3393
  Dropdown.setValue(dropdownNode, [value]);
3364
3394
  if (textNode.length > 0) {
@@ -3374,14 +3404,17 @@ class Dropdown {
3374
3404
  });
3375
3405
  }
3376
3406
  render() {
3377
- var _a;
3407
+ var _a, _b;
3378
3408
  const config = this.config;
3409
+ const defaultValue = (_a = config.defaultValue) !== null && _a !== void 0 ? _a : '';
3379
3410
  const dropdownNode = this.node;
3380
3411
  const titleNode = dropdownNode.find('.lake-dropdown-title');
3381
3412
  if (!config.downIcon) {
3382
3413
  titleNode.addClass('lake-dropdown-title-no-down');
3383
3414
  }
3384
- titleNode.css('width', config.width);
3415
+ if (config.width) {
3416
+ titleNode.css('width', config.width);
3417
+ }
3385
3418
  const tooltip = typeof config.tooltip === 'string' ? config.tooltip : config.tooltip(this.locale);
3386
3419
  titleNode.attr('title', tooltip);
3387
3420
  const textNode = titleNode.find('.lake-dropdown-text');
@@ -3398,13 +3431,20 @@ class Dropdown {
3398
3431
  }
3399
3432
  const menuNode = query('<ul class="lake-dropdown-menu" />');
3400
3433
  menuNode.addClass(`lake-${config.menuType}-dropdown-menu`);
3401
- Dropdown.setValue(dropdownNode, [config.defaultValue]);
3434
+ if (config.menuWidth) {
3435
+ menuNode.css('width', config.menuWidth);
3436
+ }
3437
+ if (config.menuHeight) {
3438
+ menuNode.addClass('lake-dropdown-menu-with-scroll');
3439
+ menuNode.css('height', config.menuHeight);
3440
+ }
3441
+ Dropdown.setValue(dropdownNode, [defaultValue]);
3402
3442
  if (textNode.length > 0) {
3403
3443
  const menuMap = Dropdown.getMenuMap(config.menuItems, this.locale);
3404
- textNode.text((_a = menuMap.get(config.defaultValue)) !== null && _a !== void 0 ? _a : config.defaultValue);
3444
+ textNode.text((_b = menuMap.get(defaultValue)) !== null && _b !== void 0 ? _b : defaultValue);
3405
3445
  }
3406
3446
  if (config.menuType === 'color') {
3407
- this.updateColorAccent(titleNode, config.defaultValue);
3447
+ this.updateColorAccent(titleNode, defaultValue);
3408
3448
  }
3409
3449
  this.apppendMenuItems(menuNode);
3410
3450
  dropdownNode.append(titleNode);
@@ -3489,21 +3529,19 @@ class BoxToolbar {
3489
3529
  // Renders a toolbar for the specified box.
3490
3530
  render() {
3491
3531
  this.root.append(this.container);
3492
- this.items.forEach(item => {
3532
+ for (const item of this.items) {
3493
3533
  if (item === '|') {
3494
3534
  this.appendDivider();
3495
- return;
3496
3535
  }
3497
- if (item.type === 'button') {
3536
+ else if (item.type === 'button') {
3498
3537
  this.buttonItemList.push(item);
3499
3538
  this.appendButton(item);
3500
- return;
3501
3539
  }
3502
- if (item.type === 'dropdown') {
3540
+ else if (item.type === 'dropdown') {
3503
3541
  this.dropdownItemList.push(item);
3504
3542
  this.appendDropdown(item);
3505
3543
  }
3506
- });
3544
+ }
3507
3545
  this.updatePosition();
3508
3546
  }
3509
3547
  unmount() {
@@ -3511,8 +3549,6 @@ class BoxToolbar {
3511
3549
  }
3512
3550
  }
3513
3551
 
3514
- // A key-value object for storing data about box.
3515
- const boxData = {};
3516
3552
  const framework = safeTemplate `
3517
3553
  <span class="lake-box-strip"><br /></span>
3518
3554
  <div class="lake-box-container" contenteditable="false"></div>
@@ -3524,7 +3560,7 @@ class Box {
3524
3560
  if (typeof node === 'string') {
3525
3561
  const component = boxes.get(node);
3526
3562
  if (component === undefined) {
3527
- throw new Error(`Box '${node}' has not been defined yet.`);
3563
+ throw new Error(`Box "${node}" has not been defined yet.`);
3528
3564
  }
3529
3565
  const type = encode(component.type);
3530
3566
  const name = encode(component.name);
@@ -3537,15 +3573,12 @@ class Box {
3537
3573
  this.node = query(node);
3538
3574
  const component = boxes.get(this.name);
3539
3575
  if (component === undefined) {
3540
- throw new Error(`Box '${this.name}' has not been defined yet.`);
3576
+ throw new Error(`Box "${this.name}" has not been defined yet.`);
3541
3577
  }
3542
3578
  if (component.value && !this.node.hasAttr('value')) {
3543
3579
  this.value = component.value;
3544
3580
  }
3545
3581
  }
3546
- if (!boxData[this.node.id]) {
3547
- boxData[this.node.id] = {};
3548
- }
3549
3582
  }
3550
3583
  // Adds the framework of the box.
3551
3584
  addFramework() {
@@ -3592,11 +3625,11 @@ class Box {
3592
3625
  if (value === '') {
3593
3626
  return {};
3594
3627
  }
3595
- return JSON.parse(Base64.decode(value));
3628
+ return JSON.parse(fromBase64(value));
3596
3629
  }
3597
3630
  // Sets the value of the box.
3598
3631
  set value(value) {
3599
- this.node.attr('value', Base64.encode(JSON.stringify(value)));
3632
+ this.node.attr('value', toBase64(JSON.stringify(value)));
3600
3633
  }
3601
3634
  updateValue(valueKey, valueValue) {
3602
3635
  const value = this.value;
@@ -3610,14 +3643,6 @@ class Box {
3610
3643
  }
3611
3644
  this.value = value;
3612
3645
  }
3613
- // Returns data of the box.
3614
- getData(key) {
3615
- return boxData[this.node.id][key];
3616
- }
3617
- // Updates data of the box.
3618
- setData(key, value) {
3619
- boxData[this.node.id][key] = value;
3620
- }
3621
3646
  // Returns the editor instance of the box.
3622
3647
  getEditor() {
3623
3648
  const container = this.node.closest('div[contenteditable]');
@@ -3678,7 +3703,6 @@ class Box {
3678
3703
  unmount() {
3679
3704
  this.event.emit('blur');
3680
3705
  this.event.emit('beforeunmount');
3681
- boxData[this.node.id] = {};
3682
3706
  this.event.removeAllListeners();
3683
3707
  this.node.empty();
3684
3708
  debug(`Box "${this.name}" (id: ${this.node.id}) unmounted`);
@@ -3852,6 +3876,71 @@ function request(option) {
3852
3876
  return xhr;
3853
3877
  }
3854
3878
 
3879
+ function uploadFile(config) {
3880
+ const { editor, name, file, onError, onSuccess } = config;
3881
+ const { requestMethod, requestAction, requestTypes } = editor.config[name];
3882
+ if (requestTypes.indexOf(file.type) < 0) {
3883
+ if (onError) {
3884
+ onError(`File "${file.name}" is not allowed for uploading.`);
3885
+ }
3886
+ throw new Error(`Cannot upload file "${file.name}" because its type "${file.type}" is not found in ['${requestTypes.join('\', \'')}'].`);
3887
+ }
3888
+ const box = editor.selection.insertBox(name, {
3889
+ url: URL.createObjectURL(file),
3890
+ status: 'uploading',
3891
+ name: file.name,
3892
+ size: file.size,
3893
+ type: file.type,
3894
+ lastModified: file.lastModified,
3895
+ });
3896
+ let xhr = request({
3897
+ onProgress: e => {
3898
+ const percentNode = box.node.find('.lake-percent');
3899
+ const percent = Math.round(e.percent);
3900
+ percentNode.text(`${percent < 100 ? percent : 99} %`);
3901
+ },
3902
+ onError: (error, body) => {
3903
+ xhr = null;
3904
+ debug(error.toString(), body);
3905
+ box.updateValue('status', 'error');
3906
+ box.render();
3907
+ if (onError) {
3908
+ onError(error.toString());
3909
+ }
3910
+ },
3911
+ onSuccess: body => {
3912
+ xhr = null;
3913
+ if (!body.url) {
3914
+ box.updateValue('status', 'error');
3915
+ box.render();
3916
+ if (onError) {
3917
+ onError('Cannot find the url field.');
3918
+ }
3919
+ return;
3920
+ }
3921
+ box.updateValue({
3922
+ status: 'done',
3923
+ url: body.url,
3924
+ });
3925
+ box.render();
3926
+ editor.history.save();
3927
+ if (onSuccess) {
3928
+ onSuccess();
3929
+ }
3930
+ },
3931
+ file,
3932
+ action: requestAction,
3933
+ method: requestMethod,
3934
+ });
3935
+ box.event.on('beforeunmount', () => {
3936
+ if (xhr) {
3937
+ xhr.abort();
3938
+ debug('Upload canceled');
3939
+ }
3940
+ });
3941
+ return box;
3942
+ }
3943
+
3855
3944
  // String
3856
3945
 
3857
3946
  var index = /*#__PURE__*/Object.freeze({
@@ -3864,6 +3953,7 @@ var index = /*#__PURE__*/Object.freeze({
3864
3953
  encode: encode,
3865
3954
  fileSize: fileSize,
3866
3955
  fixNumberedList: fixNumberedList,
3956
+ fromBase64: fromBase64,
3867
3957
  getBox: getBox,
3868
3958
  getCSS: getCSS,
3869
3959
  getDeepest: getDeepest,
@@ -3882,8 +3972,10 @@ var index = /*#__PURE__*/Object.freeze({
3882
3972
  setBlockIndent: setBlockIndent,
3883
3973
  splitNodes: splitNodes,
3884
3974
  template: template,
3975
+ toBase64: toBase64,
3885
3976
  toHex: toHex,
3886
3977
  toNodeList: toNodeList,
3978
+ uploadFile: uploadFile,
3887
3979
  wrapNodeList: wrapNodeList
3888
3980
  });
3889
3981
 
@@ -3891,6 +3983,10 @@ class Fragment {
3891
3983
  constructor(fragment) {
3892
3984
  this.fragment = fragment !== null && fragment !== void 0 ? fragment : document.createDocumentFragment();
3893
3985
  }
3986
+ // Gets a native fragment.
3987
+ get() {
3988
+ return this.fragment;
3989
+ }
3894
3990
  // Returns the descendants of the fragment which are selected by the specified CSS selector.
3895
3991
  find(selector) {
3896
3992
  const nodeList = [];
@@ -3910,7 +4006,7 @@ class Fragment {
3910
4006
  }
3911
4007
  // Inserts the specified node as the last child.
3912
4008
  append(node) {
3913
- node.each(nativeNode => {
4009
+ query(node).each(nativeNode => {
3914
4010
  this.fragment.appendChild(nativeNode);
3915
4011
  });
3916
4012
  }
@@ -4411,6 +4507,9 @@ function deleteContents(range) {
4411
4507
 
4412
4508
  // Inserts a DocumentFragment object into the specified range.
4413
4509
  function insertFragment(range, fragment) {
4510
+ if (fragment instanceof Fragment) {
4511
+ fragment = fragment.get();
4512
+ }
4414
4513
  if (range.commonAncestor.isOutside) {
4415
4514
  return;
4416
4515
  }
@@ -4428,16 +4527,6 @@ function insertFragment(range, fragment) {
4428
4527
  range.adjustBlock();
4429
4528
  }
4430
4529
 
4431
- // Inserts a HTML string into the specified range.
4432
- function insertContents(range, value) {
4433
- const nodes = query(value);
4434
- const fragment = document.createDocumentFragment();
4435
- nodes.each(nativeNode => {
4436
- fragment.appendChild(nativeNode);
4437
- });
4438
- insertFragment(range, fragment);
4439
- }
4440
-
4441
4530
  function getTopNonBlockNodes(range) {
4442
4531
  const container = range.commonAncestor.closest('div[contenteditable="true"],td');
4443
4532
  let nodeList = [];
@@ -4499,7 +4588,7 @@ function setBlocks(range, value) {
4499
4588
  }
4500
4589
  else {
4501
4590
  const block = valueNode.clone(true);
4502
- if (node.isList && node.attr('indent') !== '') {
4591
+ if (block.isList && node.isList && node.attr('indent') !== '') {
4503
4592
  block.attr('indent', node.attr('indent'));
4504
4593
  }
4505
4594
  const deepestBlock = getDeepest(block);
@@ -4951,8 +5040,6 @@ function insertLink(range, value) {
4951
5040
  return linkNode;
4952
5041
  }
4953
5042
 
4954
- var version = "0.1.14";
4955
-
4956
5043
  // Inserts a box into the specified range.
4957
5044
  function insertBox(range, boxName, boxValue) {
4958
5045
  if (range.commonAncestor.isOutside) {
@@ -5030,6 +5117,8 @@ function removeBox(range) {
5030
5117
  return box;
5031
5118
  }
5032
5119
 
5120
+ var version = "0.1.16";
5121
+
5033
5122
  // Returns the attributes of the element as an key-value object.
5034
5123
  function getAttributes(node) {
5035
5124
  const nativeNode = node.get(0);
@@ -5167,9 +5256,6 @@ class Selection {
5167
5256
  insertFragment(fragment) {
5168
5257
  return insertFragment(this.range, fragment);
5169
5258
  }
5170
- insertContents(value) {
5171
- return insertContents(this.range, value);
5172
- }
5173
5259
  deleteContents() {
5174
5260
  return deleteContents(this.range);
5175
5261
  }
@@ -5209,7 +5295,7 @@ class Selection {
5209
5295
  insertBox(boxName, boxValue) {
5210
5296
  const box = insertBox(this.range, boxName, boxValue);
5211
5297
  if (!box) {
5212
- throw new Error(`Box '${boxName}' cannot be inserted outside the editor.`);
5298
+ throw new Error(`Box "${boxName}" cannot be inserted outside the editor.`);
5213
5299
  }
5214
5300
  return box;
5215
5301
  }
@@ -5242,7 +5328,7 @@ class Command {
5242
5328
  getItem(name) {
5243
5329
  const commandItem = this.commandMap.get(name);
5244
5330
  if (commandItem === undefined) {
5245
- throw new Error(`Command '${name}' does not exist`);
5331
+ throw new Error(`Command "${name}" has not been defined yet.`);
5246
5332
  }
5247
5333
  return commandItem;
5248
5334
  }
@@ -5312,7 +5398,7 @@ class History {
5312
5398
  addIdToBoxes(node) {
5313
5399
  node.find('lake-box').each(nativeNode => {
5314
5400
  const boxNode = new Nodes(nativeNode);
5315
- const id = md5(`${boxNode.attr('type')}-${boxNode.attr('name')}-${boxNode.attr('value')}`);
5401
+ const id = `${boxNode.attr('name')}-${boxNode.attr('value')}`;
5316
5402
  boxNode.attr('id', id);
5317
5403
  });
5318
5404
  }
@@ -5553,9 +5639,9 @@ class Plugin {
5553
5639
  this.pluginList.push(plugin);
5554
5640
  }
5555
5641
  loadAll(editor) {
5556
- this.pluginList.forEach(plugin => {
5642
+ for (const plugin of this.pluginList) {
5557
5643
  plugin(editor);
5558
- });
5644
+ }
5559
5645
  }
5560
5646
  }
5561
5647
 
@@ -5568,6 +5654,7 @@ const defaultConfig = {
5568
5654
  indentWithTab: true,
5569
5655
  lang: 'en-US',
5570
5656
  minChangeSize: 5,
5657
+ historySize: 100,
5571
5658
  onMessage: (type, message) => {
5572
5659
  if (type === 'success') {
5573
5660
  // eslint-disable-next-line no-console
@@ -5679,11 +5766,8 @@ class Editor {
5679
5766
  boxContainer.removeClass('lake-box-selected');
5680
5767
  box.event.emit('blur');
5681
5768
  });
5682
- this.event.emit('boxselectionstylechange');
5683
5769
  }, 50, {
5684
- leading: false,
5685
- trailing: true,
5686
- maxWait: 50,
5770
+ immediate: true,
5687
5771
  });
5688
5772
  // Triggers the statechange event when the current selection of the editor is changed.
5689
5773
  this.emitStateChangeEvent = debounce(() => {
@@ -5727,10 +5811,8 @@ class Editor {
5727
5811
  }
5728
5812
  this.event.emit('statechange', state);
5729
5813
  this.state = state;
5730
- }, 100, {
5731
- leading: false,
5732
- trailing: true,
5733
- maxWait: 100,
5814
+ }, 50, {
5815
+ immediate: false,
5734
5816
  });
5735
5817
  if (!config.root) {
5736
5818
  throw new Error('The root of the config must be specified.');
@@ -5758,6 +5840,7 @@ class Editor {
5758
5840
  this.selection = new Selection(this.container);
5759
5841
  this.command = new Command(this.selection);
5760
5842
  this.history = new History(this.selection);
5843
+ this.history.limit = this.config.historySize;
5761
5844
  this.keystroke = new Keystroke(this.container);
5762
5845
  editors.set(this.container.id, this);
5763
5846
  }
@@ -5983,8 +6066,12 @@ class Editor {
5983
6066
  }
5984
6067
  // Scrolls to the caret or the range of the selection.
5985
6068
  scrollToCaret() {
6069
+ const range = this.selection.range;
6070
+ if (range.isBox) {
6071
+ return;
6072
+ }
5986
6073
  // Creates an artificial caret that is the same size as the caret at the current caret position.
5987
- const rangeRect = this.selection.range.getRect();
6074
+ const rangeRect = range.getRect();
5988
6075
  if (rangeRect.x === 0 || rangeRect.y === 0) {
5989
6076
  return;
5990
6077
  }
@@ -6281,7 +6368,7 @@ const moreStyleMenuItems = [
6281
6368
  text: locale => locale.toolbar.code(),
6282
6369
  },
6283
6370
  ];
6284
- // These colors are from Ant Design (https://ant.design/docs/spec/colors)
6371
+ // These colors are sourced from Ant Design (https://ant.design/docs/spec/colors)
6285
6372
  const colors = [
6286
6373
  // Dust Red, Volcano, Sunset Orange, Calendula Gold, Sunrise Yellow, Lime, Polar Green, Cyan, Daybreak Blue, Geek Blue, Golden Purple, Magenta
6287
6374
  '#f5222d', '#fa541c', '#fa8c16', '#faad14', '#fadb14', '#a0d911', '#52c41a', '#13c2c2', '#1677ff', '#2f54eb', '#722ed1', '#eb2f96', // color 6
@@ -6556,19 +6643,19 @@ const toolbarItems = [
6556
6643
  },
6557
6644
  },
6558
6645
  {
6559
- name: 'video',
6646
+ name: 'codeBlock',
6560
6647
  type: 'button',
6561
- icon: icons.get('video'),
6562
- tooltip: locale => locale.toolbar.video(),
6648
+ icon: icons.get('codeBlock'),
6649
+ tooltip: locale => locale.toolbar.codeBlock(),
6563
6650
  onClick: (editor, value) => {
6564
6651
  editor.command.execute(value);
6565
6652
  },
6566
6653
  },
6567
6654
  {
6568
- name: 'codeBlock',
6655
+ name: 'video',
6569
6656
  type: 'button',
6570
- icon: icons.get('codeBlock'),
6571
- tooltip: locale => locale.toolbar.codeBlock(),
6657
+ icon: icons.get('video'),
6658
+ tooltip: locale => locale.toolbar.video(),
6572
6659
  onClick: (editor, value) => {
6573
6660
  editor.command.execute(value);
6574
6661
  },
@@ -6591,9 +6678,7 @@ const toolbarItems = [
6591
6678
  type: 'dropdown',
6592
6679
  downIcon: icons.get('down'),
6593
6680
  icon: icons.get('list'),
6594
- defaultValue: '',
6595
6681
  tooltip: locale => locale.toolbar.list(),
6596
- width: 'auto',
6597
6682
  menuType: 'list',
6598
6683
  menuItems: listMenuItems,
6599
6684
  onSelect: (editor, value) => {
@@ -6605,9 +6690,7 @@ const toolbarItems = [
6605
6690
  type: 'dropdown',
6606
6691
  downIcon: icons.get('down'),
6607
6692
  icon: icons.get('alignLeft'),
6608
- defaultValue: '',
6609
6693
  tooltip: locale => locale.toolbar.align(),
6610
- width: 'auto',
6611
6694
  menuType: 'list',
6612
6695
  menuItems: alignMenuItems,
6613
6696
  onSelect: (editor, value) => {
@@ -6619,9 +6702,7 @@ const toolbarItems = [
6619
6702
  type: 'dropdown',
6620
6703
  downIcon: icons.get('down'),
6621
6704
  icon: icons.get('increaseIndent'),
6622
- defaultValue: '',
6623
6705
  tooltip: locale => locale.toolbar.indent(),
6624
- width: 'auto',
6625
6706
  menuType: 'list',
6626
6707
  menuItems: indentMenuItems,
6627
6708
  onSelect: (editor, value) => {
@@ -6658,9 +6739,7 @@ const toolbarItems = [
6658
6739
  name: 'moreStyle',
6659
6740
  type: 'dropdown',
6660
6741
  icon: icons.get('more'),
6661
- defaultValue: '',
6662
6742
  tooltip: locale => locale.toolbar.moreStyle(),
6663
- width: 'auto',
6664
6743
  menuType: 'list',
6665
6744
  menuItems: moreStyleMenuItems,
6666
6745
  selectedValues: appliedItems => {
@@ -6686,9 +6765,9 @@ const toolbarItems = [
6686
6765
  accentIcon: icons.get('fontColorAccent'),
6687
6766
  defaultValue: '#f5222d',
6688
6767
  tooltip: locale => locale.toolbar.fontColor(),
6689
- width: 'auto',
6690
6768
  menuType: 'color',
6691
6769
  menuItems: colorMenuItems,
6770
+ menuWidth: '296px',
6692
6771
  onSelect: (editor, value) => {
6693
6772
  editor.command.execute('fontColor', value);
6694
6773
  },
@@ -6701,9 +6780,9 @@ const toolbarItems = [
6701
6780
  accentIcon: icons.get('highlightAccent'),
6702
6781
  defaultValue: '#fadb14',
6703
6782
  tooltip: locale => locale.toolbar.highlight(),
6704
- width: 'auto',
6705
6783
  menuType: 'color',
6706
6784
  menuItems: colorMenuItems,
6785
+ menuWidth: '296px',
6707
6786
  onSelect: (editor, value) => {
6708
6787
  editor.command.execute('highlight', value);
6709
6788
  },
@@ -6726,64 +6805,6 @@ const toolbarItems = [
6726
6805
  },
6727
6806
  ];
6728
6807
 
6729
- function uploadFile(config) {
6730
- const { editor, name, file, onError, onSuccess } = config;
6731
- const { requestMethod, requestAction, requestTypes } = editor.config[name];
6732
- if (requestTypes.indexOf(file.type) < 0) {
6733
- if (onError) {
6734
- onError(`File '${file.name}' is not allowed for uploading.`);
6735
- }
6736
- throw new Error(`Cannot upload file '${file.name}' because its type '${file.type}' is not found in ['${requestTypes.join('\', \'')}'].`);
6737
- }
6738
- const box = editor.selection.insertBox(name, {
6739
- url: URL.createObjectURL(file),
6740
- status: 'uploading',
6741
- name: file.name,
6742
- size: file.size,
6743
- type: file.type,
6744
- lastModified: file.lastModified,
6745
- });
6746
- const xhr = request({
6747
- onProgress: e => {
6748
- const percentNode = box.node.find('.lake-percent');
6749
- const percent = Math.round(e.percent);
6750
- percentNode.text(`${percent < 100 ? percent : 99} %`);
6751
- },
6752
- onError: (error, body) => {
6753
- debug(error.toString(), body);
6754
- box.updateValue('status', 'error');
6755
- box.render();
6756
- if (onError) {
6757
- onError(error.toString());
6758
- }
6759
- },
6760
- onSuccess: body => {
6761
- if (!body.url) {
6762
- box.updateValue('status', 'error');
6763
- box.render();
6764
- if (onError) {
6765
- onError('Cannot find the url field.');
6766
- }
6767
- return;
6768
- }
6769
- box.updateValue({
6770
- status: 'done',
6771
- url: body.url,
6772
- });
6773
- box.render();
6774
- editor.history.save();
6775
- if (onSuccess) {
6776
- onSuccess();
6777
- }
6778
- },
6779
- file,
6780
- action: requestAction,
6781
- method: requestMethod,
6782
- });
6783
- box.setData('xhr', xhr);
6784
- return box;
6785
- }
6786
-
6787
6808
  const defaultItems = [
6788
6809
  'undo',
6789
6810
  'redo',
@@ -6806,9 +6827,9 @@ const defaultItems = [
6806
6827
  'hr',
6807
6828
  ];
6808
6829
  const toolbarItemMap = new Map();
6809
- toolbarItems.forEach(item => {
6830
+ for (const item of toolbarItems) {
6810
6831
  toolbarItemMap.set(item.name, item);
6811
- });
6832
+ }
6812
6833
  class Toolbar {
6813
6834
  constructor(config) {
6814
6835
  this.placement = 'top';
@@ -6853,6 +6874,8 @@ class Toolbar {
6853
6874
  width: item.width,
6854
6875
  menuType: item.menuType,
6855
6876
  menuItems: item.menuItems,
6877
+ menuWidth: item.menuWidth,
6878
+ menuHeight: item.menuHeight,
6856
6879
  tabIndex: -1,
6857
6880
  placement: this.placement === 'top' ? 'bottom' : 'top',
6858
6881
  onSelect: value => {
@@ -6954,7 +6977,7 @@ class Toolbar {
6954
6977
  Dropdown.setValue(dropdownNode, selectedValues);
6955
6978
  const textNode = dropdownNode.find('.lake-dropdown-text');
6956
6979
  if (textNode.length > 0) {
6957
- const key = selectedValues[0] || item.defaultValue;
6980
+ const key = selectedValues[0] || item.defaultValue || '';
6958
6981
  const menuMap = this.allMenuMap.get(item.name);
6959
6982
  const text = (_a = (menuMap && menuMap.get(key))) !== null && _a !== void 0 ? _a : key;
6960
6983
  textNode.text(text);
@@ -6966,36 +6989,35 @@ class Toolbar {
6966
6989
  render(editor) {
6967
6990
  this.root.empty();
6968
6991
  this.root.append(this.container);
6969
- this.items.forEach(name => {
6992
+ for (const name of this.items) {
6970
6993
  if (name === '|') {
6971
6994
  this.appendDivider();
6972
- return;
6973
- }
6974
- let item;
6975
- if (typeof name === 'string') {
6976
- item = toolbarItemMap.get(name);
6977
- if (!item) {
6978
- return;
6979
- }
6980
6995
  }
6981
6996
  else {
6982
- item = name;
6983
- }
6984
- if (item.type === 'button') {
6985
- this.buttonItemList.push(item);
6986
- this.appendButton(editor, item);
6987
- return;
6988
- }
6989
- if (item.type === 'dropdown') {
6990
- this.allMenuMap.set(item.name, Dropdown.getMenuMap(item.menuItems, editor.locale));
6991
- this.dropdownItemList.push(item);
6992
- this.appendDropdown(editor, item);
6993
- return;
6994
- }
6995
- if (item.type === 'upload') {
6996
- this.appendUpload(editor, item);
6997
+ let item;
6998
+ if (typeof name === 'string') {
6999
+ item = toolbarItemMap.get(name);
7000
+ if (!item) {
7001
+ throw new Error(`ToolbarItem "${name}" has not been defined yet.`);
7002
+ }
7003
+ }
7004
+ else {
7005
+ item = name;
7006
+ }
7007
+ if (item.type === 'button') {
7008
+ this.buttonItemList.push(item);
7009
+ this.appendButton(editor, item);
7010
+ }
7011
+ else if (item.type === 'dropdown') {
7012
+ this.allMenuMap.set(item.name, Dropdown.getMenuMap(item.menuItems, editor.locale));
7013
+ this.dropdownItemList.push(item);
7014
+ this.appendDropdown(editor, item);
7015
+ }
7016
+ else if (item.type === 'upload') {
7017
+ this.appendUpload(editor, item);
7018
+ }
6997
7019
  }
6998
- });
7020
+ }
6999
7021
  }
7000
7022
  }
7001
7023
 
@@ -7007,247 +7029,15 @@ const hrBox = {
7007
7029
  if (!editor) {
7008
7030
  return;
7009
7031
  }
7010
- const hrNode = query('<div class="lake-hr"><hr /></div>');
7011
- box.getContainer().append(hrNode);
7012
- hrNode.on('click', () => {
7032
+ const rootNode = query('<div class="lake-hr"><hr /></div>');
7033
+ box.getContainer().append(rootNode);
7034
+ rootNode.on('click', () => {
7013
7035
  editor.selection.selectBox(box);
7014
7036
  });
7015
7037
  },
7016
7038
  html: () => '<hr />',
7017
7039
  };
7018
7040
 
7019
- class BoxResizer {
7020
- constructor(config) {
7021
- this.config = config;
7022
- this.root = config.root;
7023
- this.box = config.box;
7024
- }
7025
- bindEvents(pointerNode) {
7026
- const box = this.box;
7027
- const boxContainer = box.getContainer();
7028
- const resizerNode = pointerNode.closest('.lake-resizer');
7029
- const infoNode = resizerNode.find('.lake-resizer-info');
7030
- const isPlus = pointerNode.attr('class').indexOf('-right') >= 0;
7031
- const initialWidth = boxContainer.width();
7032
- const initialHeight = boxContainer.height();
7033
- const rate = initialHeight / initialWidth;
7034
- let clientX = 0;
7035
- let width = 0;
7036
- // resizing box
7037
- const pointermoveListener = (event) => {
7038
- const pointerEvent = event;
7039
- const diffX = pointerEvent.clientX - clientX;
7040
- const newWidth = Math.round(isPlus ? width + diffX : width - diffX);
7041
- const newHeight = Math.round(rate * newWidth);
7042
- infoNode.text(`${newWidth} x ${newHeight}`);
7043
- boxContainer.css({
7044
- width: `${newWidth}px`,
7045
- height: `${newHeight}px`,
7046
- });
7047
- if (this.config.onResize) {
7048
- this.config.onResize(newWidth, newHeight);
7049
- }
7050
- };
7051
- // start resizing
7052
- const pointerdownListener = (event) => {
7053
- const pointerEvent = event;
7054
- const pointerNativeNode = pointerNode.get(0);
7055
- // The capture will be implicitly released after a pointerup or pointercancel event.
7056
- // https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
7057
- pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
7058
- clientX = pointerEvent.clientX;
7059
- width = boxContainer.width();
7060
- infoNode.show();
7061
- pointerNode.on('pointermove', pointermoveListener);
7062
- };
7063
- // stop resizing
7064
- const pointerupListner = () => {
7065
- pointerNode.off('pointermove');
7066
- infoNode.hide();
7067
- width = box.getContainer().width();
7068
- const height = Math.round(rate * width);
7069
- this.config.onStop(width, height);
7070
- };
7071
- // cancel resizing
7072
- const pointercancelListner = () => {
7073
- pointerNode.off('pointermove');
7074
- infoNode.hide();
7075
- };
7076
- pointerNode.on('pointerdown', pointerdownListener);
7077
- pointerNode.on('pointerup', pointerupListner);
7078
- pointerNode.on('pointercancel', pointercancelListner);
7079
- }
7080
- render() {
7081
- const { width, height } = this.config;
7082
- const resizerNode = query(safeTemplate `
7083
- <div class="lake-resizer">
7084
- <div class="lake-resizer-top-left"></div>
7085
- <div class="lake-resizer-top-right"></div>
7086
- <div class="lake-resizer-bottom-left"></div>
7087
- <div class="lake-resizer-bottom-right"></div>
7088
- <div class="lake-resizer-info">${width} x ${height}</div>
7089
- </div>
7090
- `);
7091
- this.bindEvents(resizerNode.find('.lake-resizer-top-left'));
7092
- this.bindEvents(resizerNode.find('.lake-resizer-top-right'));
7093
- this.bindEvents(resizerNode.find('.lake-resizer-bottom-left'));
7094
- this.bindEvents(resizerNode.find('.lake-resizer-bottom-right'));
7095
- this.root.append(resizerNode);
7096
- }
7097
- }
7098
-
7099
- function getVideoId(url) {
7100
- const result = /\w+$/i.exec(url || '');
7101
- return result ? result[0] : '';
7102
- }
7103
- function getInputValue(videoNode, name) {
7104
- const inputElement = videoNode.find(`input[name="${name}"]`);
7105
- const nativeInputElement = inputElement.get(0);
7106
- return nativeInputElement.value;
7107
- }
7108
- function appendButtonGroup(box) {
7109
- const editor = box.getEditor();
7110
- if (!editor) {
7111
- return;
7112
- }
7113
- const boxContainer = box.getContainer();
7114
- const videoNode = boxContainer.find('.lake-video');
7115
- const buttonGroupNode = query(safeTemplate `
7116
- <div class="lake-button-group">
7117
- <button type="button" tabindex="-1" class="lake-button-remove" title="${editor.locale.video.remove()}"></button>
7118
- </div>
7119
- `);
7120
- const removeButton = buttonGroupNode.find('.lake-button-remove');
7121
- const removeIcon = icons.get('remove');
7122
- if (removeIcon) {
7123
- removeButton.append(removeIcon);
7124
- }
7125
- buttonGroupNode.find('.lake-button-remove').on('click', event => {
7126
- event.stopPropagation();
7127
- editor.selection.removeBox(box);
7128
- editor.history.save();
7129
- });
7130
- videoNode.append(buttonGroupNode);
7131
- }
7132
- function showVideo(box) {
7133
- const editor = box.getEditor();
7134
- if (!editor) {
7135
- return;
7136
- }
7137
- const boxContainer = box.getContainer();
7138
- const value = box.value;
7139
- const width = value.width || 560;
7140
- const height = value.height || 315;
7141
- boxContainer.css({
7142
- width: `${width}px`,
7143
- height: `${height}px`,
7144
- });
7145
- const videoId = getVideoId(value.url);
7146
- if (videoId === '') {
7147
- throw new Error(`Invalid link: ${value.url}`);
7148
- }
7149
- // YouTube URL: https://www.youtube.com/watch?v=5sMBhDv4sik
7150
- // The script for embedding YouTube:
7151
- // <iframe width="560" height="315" src="https://www.youtube.com/embed/5sMBhDv4sik" title="YouTube video player"
7152
- // frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7153
- // referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7154
- const iframeNode = query(safeTemplate `
7155
- <iframe width="100%" height="${height}" src="https://www.youtube.com/embed/${videoId}" title="YouTube video player"
7156
- frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7157
- referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7158
- `);
7159
- const videoNode = boxContainer.find('.lake-video');
7160
- if (!editor.readonly) {
7161
- iframeNode.on('load', () => {
7162
- appendButtonGroup(box);
7163
- new BoxResizer({
7164
- root: videoNode,
7165
- box,
7166
- width,
7167
- height,
7168
- onResize: (newWidth, newHeight) => {
7169
- iframeNode.attr({
7170
- height: newHeight.toString(),
7171
- });
7172
- },
7173
- onStop: (newWidth, newHeight) => {
7174
- box.updateValue({
7175
- width: newWidth,
7176
- height: newHeight,
7177
- });
7178
- editor.history.save();
7179
- },
7180
- }).render();
7181
- });
7182
- }
7183
- videoNode.append(iframeNode);
7184
- }
7185
- const videoBox = {
7186
- type: 'inline',
7187
- name: 'video',
7188
- render: box => {
7189
- const editor = box.getEditor();
7190
- if (!editor) {
7191
- return;
7192
- }
7193
- const locale = editor.locale;
7194
- const value = box.value;
7195
- const boxContainer = box.getContainer();
7196
- const videoNode = query('<div class="lake-video" />');
7197
- boxContainer.empty();
7198
- boxContainer.css({
7199
- width: '',
7200
- height: '',
7201
- });
7202
- boxContainer.append(videoNode);
7203
- if (!value.url) {
7204
- if (editor.readonly) {
7205
- box.node.hide();
7206
- return;
7207
- }
7208
- const formNode = query(safeTemplate `
7209
- <div class="lake-video-form">
7210
- <div class="lake-row lake-desc-row">${locale.video.description()}</div>
7211
- <div class="lake-row">${locale.video.url()}</div>
7212
- <div class="lake-row">
7213
- <input type="text" name="url" placeholder="https://www.youtube.com/watch?v=..." />
7214
- </div>
7215
- <div class="lake-row lake-button-row"></div>
7216
- </div>
7217
- `);
7218
- const button = new Button({
7219
- root: formNode.find('.lake-button-row'),
7220
- name: 'embed',
7221
- type: 'primary',
7222
- text: locale.video.embed(),
7223
- onClick: () => {
7224
- const url = getInputValue(formNode, 'url');
7225
- if (url.indexOf('https://www.youtube.com/') < 0 || getVideoId(url) === '') {
7226
- editor.config.onMessage('error', locale.video.urlError());
7227
- return;
7228
- }
7229
- box.updateValue('url', url);
7230
- editor.history.save();
7231
- formNode.remove();
7232
- showVideo(box);
7233
- },
7234
- });
7235
- formNode.find('input[name="url"]').on('keydown', createKeybindingsHandler({
7236
- 'Enter': event => {
7237
- event.preventDefault();
7238
- button.node.emit('click');
7239
- },
7240
- }));
7241
- button.render();
7242
- videoNode.append(formNode);
7243
- appendButtonGroup(box);
7244
- }
7245
- else {
7246
- showVideo(box);
7247
- }
7248
- },
7249
- };
7250
-
7251
7041
  const config = {
7252
7042
  comment: '#57606a',
7253
7043
  name: '#444d56',
@@ -7334,12 +7124,12 @@ const codeBlockBox = {
7334
7124
  if (!editor) {
7335
7125
  return;
7336
7126
  }
7337
- const codeBlockNode = query('<div class="lake-code-block" />');
7127
+ const rootNode = query('<div class="lake-code-block" />');
7338
7128
  const container = box.getContainer();
7339
7129
  container.css('width', `${editor.container.innerWidth() - 2}px`);
7340
7130
  container.empty();
7341
- container.append(codeBlockNode);
7342
- const codeBlockNativeNode = codeBlockNode.get(0);
7131
+ container.append(rootNode);
7132
+ const codeBlockNativeNode = rootNode.get(0);
7343
7133
  if (!codeBlockNativeNode) {
7344
7134
  return;
7345
7135
  }
@@ -7350,12 +7140,12 @@ const codeBlockBox = {
7350
7140
  box.node.hide();
7351
7141
  return;
7352
7142
  }
7353
- codeBlockNode.addClass('lake-code-block-error');
7354
- codeBlockNode.text(`
7143
+ rootNode.addClass('lake-code-block-error');
7144
+ rootNode.text(`
7355
7145
  The code cannot be displayed because window.LakeCodeMirror is not found.
7356
7146
  Please check if the "lake-codemirror" library is added to this page.
7357
7147
  `.trim());
7358
- codeBlockNode.on('click', () => {
7148
+ rootNode.on('click', () => {
7359
7149
  editor.selection.selectBox(box);
7360
7150
  });
7361
7151
  return;
@@ -7405,36 +7195,40 @@ const codeBlockBox = {
7405
7195
  updateListener,
7406
7196
  ],
7407
7197
  });
7408
- codeBlockNode.find('[contenteditable="true"]').attr('tabindex', '-1');
7198
+ rootNode.find('[contenteditable="true"]').attr('tabindex', '-1');
7409
7199
  const dropdown = new Dropdown({
7410
- root: codeBlockNode,
7200
+ root: rootNode,
7411
7201
  name: 'langType',
7412
7202
  downIcon: icons.get('down'),
7413
7203
  defaultValue: langItem ? boxValue.lang : codeBlockConfig.defaultLang,
7414
7204
  tooltip: editor.locale.codeBlock.langType(),
7415
- width: 'auto',
7416
7205
  menuType: 'list',
7206
+ menuHeight: '200px',
7417
7207
  menuItems: langItems.map((item) => ({
7418
7208
  value: item.value,
7419
7209
  text: item.text,
7420
7210
  })),
7421
7211
  onSelect: value => {
7422
- const item = langItemMap.get(value);
7423
- codeEditor.dispatch({
7424
- effects: language.reconfigure(item && item.component ? item.component() : []),
7425
- });
7426
7212
  box.updateValue({
7427
7213
  lang: value,
7428
7214
  });
7215
+ box.unmount();
7216
+ box.render();
7217
+ editor.selection.selectBox(box);
7429
7218
  editor.history.save();
7430
7219
  },
7431
7220
  });
7432
7221
  dropdown.render();
7433
- box.setData('codeEditor', codeEditor);
7434
7222
  const resizeListener = () => {
7435
7223
  container.css('width', `${editor.container.innerWidth() - 2}px`);
7436
7224
  };
7437
7225
  editor.event.on('resize', resizeListener);
7226
+ rootNode.on('click', () => {
7227
+ if (codeEditor.hasFocus) {
7228
+ return;
7229
+ }
7230
+ codeEditor.focus();
7231
+ });
7438
7232
  box.event.on('beforeunmount', () => {
7439
7233
  codeEditor.destroy();
7440
7234
  editor.event.off('resize', resizeListener);
@@ -7475,6 +7269,86 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
7475
7269
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
7476
7270
  };
7477
7271
 
7272
+ class BoxResizer {
7273
+ constructor(config) {
7274
+ this.config = config;
7275
+ this.root = config.root;
7276
+ this.box = config.box;
7277
+ }
7278
+ bindEvents(pointerNode) {
7279
+ const box = this.box;
7280
+ const boxContainer = box.getContainer();
7281
+ const resizerNode = pointerNode.closest('.lake-resizer');
7282
+ const infoNode = resizerNode.find('.lake-resizer-info');
7283
+ const isPlus = pointerNode.attr('class').indexOf('-right') >= 0;
7284
+ const initialWidth = boxContainer.width();
7285
+ const initialHeight = boxContainer.height();
7286
+ const rate = initialHeight / initialWidth;
7287
+ let clientX = 0;
7288
+ let width = 0;
7289
+ // resizing box
7290
+ const pointermoveListener = (event) => {
7291
+ const pointerEvent = event;
7292
+ const diffX = pointerEvent.clientX - clientX;
7293
+ const newWidth = Math.round(isPlus ? width + diffX : width - diffX);
7294
+ const newHeight = Math.round(rate * newWidth);
7295
+ infoNode.text(`${newWidth} x ${newHeight}`);
7296
+ boxContainer.css({
7297
+ width: `${newWidth}px`,
7298
+ height: `${newHeight}px`,
7299
+ });
7300
+ if (this.config.onResize) {
7301
+ this.config.onResize(newWidth, newHeight);
7302
+ }
7303
+ };
7304
+ // start resizing
7305
+ const pointerdownListener = (event) => {
7306
+ const pointerEvent = event;
7307
+ const pointerNativeNode = pointerNode.get(0);
7308
+ // The capture will be implicitly released after a pointerup or pointercancel event.
7309
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
7310
+ pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
7311
+ clientX = pointerEvent.clientX;
7312
+ width = boxContainer.width();
7313
+ infoNode.show();
7314
+ pointerNode.on('pointermove', pointermoveListener);
7315
+ };
7316
+ // stop resizing
7317
+ const pointerupListner = () => {
7318
+ pointerNode.off('pointermove');
7319
+ infoNode.hide();
7320
+ width = box.getContainer().width();
7321
+ const height = Math.round(rate * width);
7322
+ this.config.onStop(width, height);
7323
+ };
7324
+ // cancel resizing
7325
+ const pointercancelListner = () => {
7326
+ pointerNode.off('pointermove');
7327
+ infoNode.hide();
7328
+ };
7329
+ pointerNode.on('pointerdown', pointerdownListener);
7330
+ pointerNode.on('pointerup', pointerupListner);
7331
+ pointerNode.on('pointercancel', pointercancelListner);
7332
+ }
7333
+ render() {
7334
+ const { width, height } = this.config;
7335
+ const resizerNode = query(safeTemplate `
7336
+ <div class="lake-resizer">
7337
+ <div class="lake-resizer-top-left"></div>
7338
+ <div class="lake-resizer-top-right"></div>
7339
+ <div class="lake-resizer-bottom-left"></div>
7340
+ <div class="lake-resizer-bottom-right"></div>
7341
+ <div class="lake-resizer-info">${width} x ${height}</div>
7342
+ </div>
7343
+ `);
7344
+ this.bindEvents(resizerNode.find('.lake-resizer-top-left'));
7345
+ this.bindEvents(resizerNode.find('.lake-resizer-top-right'));
7346
+ this.bindEvents(resizerNode.find('.lake-resizer-bottom-left'));
7347
+ this.bindEvents(resizerNode.find('.lake-resizer-bottom-right'));
7348
+ this.root.append(resizerNode);
7349
+ }
7350
+ }
7351
+
7478
7352
  // Loads an image and get its width and height.
7479
7353
  function getImageInfo(url) {
7480
7354
  return __awaiter(this, void 0, void 0, function* () {
@@ -7612,7 +7486,7 @@ function openFullScreen(box) {
7612
7486
  lightbox.loadAndOpen(currentIndex);
7613
7487
  }
7614
7488
  // Displays error icon and filename.
7615
- function renderError(imageNode, box) {
7489
+ function renderError(rootNode, box) {
7616
7490
  return __awaiter(this, void 0, void 0, function* () {
7617
7491
  const editor = box.getEditor();
7618
7492
  if (!editor) {
@@ -7643,12 +7517,12 @@ function renderError(imageNode, box) {
7643
7517
  if (imageIcon) {
7644
7518
  errorNode.find('.lake-error-icon').append(imageIcon);
7645
7519
  }
7646
- imageNode.append(buttonGroupNode);
7647
- imageNode.append(errorNode);
7520
+ rootNode.append(buttonGroupNode);
7521
+ rootNode.append(errorNode);
7648
7522
  });
7649
7523
  }
7650
7524
  // Displays an image with uplaoding progress.
7651
- function renderUploading(imageNode, box) {
7525
+ function renderUploading(rootNode, box) {
7652
7526
  return __awaiter(this, void 0, void 0, function* () {
7653
7527
  const editor = box.getEditor();
7654
7528
  if (!editor) {
@@ -7657,7 +7531,7 @@ function renderUploading(imageNode, box) {
7657
7531
  const value = box.value;
7658
7532
  const imageInfo = yield getImageInfo(value.url);
7659
7533
  if (!imageInfo.width || !imageInfo.height) {
7660
- yield renderError(imageNode, box);
7534
+ yield renderError(rootNode, box);
7661
7535
  return;
7662
7536
  }
7663
7537
  const maxWidth = editor.container.innerWidth() - 2;
@@ -7702,13 +7576,13 @@ function renderUploading(imageNode, box) {
7702
7576
  draggable: 'false',
7703
7577
  alt: value.name,
7704
7578
  });
7705
- imageNode.append(buttonGroupNode);
7706
- imageNode.append(progressNode);
7707
- imageNode.append(imgNode);
7579
+ rootNode.append(buttonGroupNode);
7580
+ rootNode.append(progressNode);
7581
+ rootNode.append(imgNode);
7708
7582
  });
7709
7583
  }
7710
7584
  // Displays an image that can be previewed or removed.
7711
- function renderDone(imageNode, box) {
7585
+ function renderDone(rootNode, box) {
7712
7586
  return __awaiter(this, void 0, void 0, function* () {
7713
7587
  const editor = box.getEditor();
7714
7588
  if (!editor) {
@@ -7721,7 +7595,7 @@ function renderDone(imageNode, box) {
7721
7595
  return;
7722
7596
  }
7723
7597
  if (!imageInfo.width || !imageInfo.height) {
7724
- yield renderError(imageNode, box);
7598
+ yield renderError(rootNode, box);
7725
7599
  return;
7726
7600
  }
7727
7601
  let width = value.width;
@@ -7764,9 +7638,9 @@ function renderDone(imageNode, box) {
7764
7638
  draggable: 'false',
7765
7639
  alt: value.name,
7766
7640
  });
7767
- imageNode.append(buttonGroupNode);
7641
+ rootNode.append(buttonGroupNode);
7768
7642
  new BoxResizer({
7769
- root: imageNode,
7643
+ root: rootNode,
7770
7644
  box,
7771
7645
  width,
7772
7646
  height,
@@ -7778,7 +7652,7 @@ function renderDone(imageNode, box) {
7778
7652
  editor.history.save();
7779
7653
  },
7780
7654
  }).render();
7781
- imageNode.append(imgNode);
7655
+ rootNode.append(imgNode);
7782
7656
  });
7783
7657
  }
7784
7658
  const imageBox = {
@@ -7819,45 +7693,193 @@ const imageBox = {
7819
7693
  if (value.status === 'loading') {
7820
7694
  return;
7821
7695
  }
7822
- const imageNode = query('<div class="lake-image" />');
7823
- imageNode.addClass(`lake-image-${value.status}`);
7696
+ const rootNode = query('<div class="lake-image" />');
7697
+ rootNode.addClass(`lake-image-${value.status}`);
7824
7698
  let promise;
7825
7699
  if (value.status === 'uploading') {
7826
- promise = renderUploading(imageNode, box);
7700
+ promise = renderUploading(rootNode, box);
7827
7701
  }
7828
7702
  else if (value.status === 'error') {
7829
- promise = renderError(imageNode, box);
7703
+ promise = renderError(rootNode, box);
7830
7704
  }
7831
7705
  else {
7832
- promise = renderDone(imageNode, box);
7706
+ promise = renderDone(rootNode, box);
7833
7707
  }
7834
7708
  promise.then(() => {
7835
7709
  container.empty();
7836
- container.append(imageNode);
7837
- imageNode.find('.lake-button-view').on('click', () => openFullScreen(box));
7710
+ container.append(rootNode);
7711
+ rootNode.find('.lake-button-view').on('click', () => openFullScreen(box));
7838
7712
  if (editor.readonly) {
7839
- imageNode.find('.lake-button-remove').hide();
7713
+ rootNode.find('.lake-button-remove').hide();
7840
7714
  }
7841
7715
  else {
7842
- imageNode.find('.lake-button-remove').on('click', event => {
7716
+ rootNode.find('.lake-button-remove').on('click', event => {
7843
7717
  event.stopPropagation();
7844
- const xhr = box.getData('xhr');
7845
- if (xhr) {
7846
- xhr.abort();
7847
- }
7848
7718
  editor.selection.removeBox(box);
7849
7719
  editor.history.save();
7850
7720
  });
7851
7721
  }
7852
7722
  box.event.emit('render');
7853
7723
  });
7854
- imageNode.on('click', () => {
7724
+ rootNode.on('click', () => {
7855
7725
  editor.selection.selectBox(box);
7856
7726
  });
7857
7727
  },
7858
7728
  html: box => {
7859
- const value = box.node.attr('value');
7860
- return safeTemplate `<img src="${box.value.url}" data-lake-value="${value}" />`;
7729
+ const rawValue = box.node.attr('value');
7730
+ return safeTemplate `<img src="${box.value.url}" data-lake-value="${rawValue}" />`;
7731
+ },
7732
+ };
7733
+
7734
+ function getVideoId(url) {
7735
+ const result = /\w+$/i.exec(url || '');
7736
+ return result ? result[0] : '';
7737
+ }
7738
+ function getInputValue(videoNode, name) {
7739
+ const inputElement = videoNode.find(`input[name="${name}"]`);
7740
+ const nativeInputElement = inputElement.get(0);
7741
+ return nativeInputElement.value;
7742
+ }
7743
+ function appendButtonGroup(box) {
7744
+ const editor = box.getEditor();
7745
+ if (!editor) {
7746
+ return;
7747
+ }
7748
+ const boxContainer = box.getContainer();
7749
+ const videoNode = boxContainer.find('.lake-video');
7750
+ const buttonGroupNode = query(safeTemplate `
7751
+ <div class="lake-button-group">
7752
+ <button type="button" tabindex="-1" class="lake-button-remove" title="${editor.locale.video.remove()}"></button>
7753
+ </div>
7754
+ `);
7755
+ const removeButton = buttonGroupNode.find('.lake-button-remove');
7756
+ const removeIcon = icons.get('remove');
7757
+ if (removeIcon) {
7758
+ removeButton.append(removeIcon);
7759
+ }
7760
+ buttonGroupNode.find('.lake-button-remove').on('click', event => {
7761
+ event.stopPropagation();
7762
+ editor.selection.removeBox(box);
7763
+ editor.history.save();
7764
+ });
7765
+ videoNode.append(buttonGroupNode);
7766
+ }
7767
+ function showVideo(box) {
7768
+ const editor = box.getEditor();
7769
+ if (!editor) {
7770
+ return;
7771
+ }
7772
+ const boxContainer = box.getContainer();
7773
+ const value = box.value;
7774
+ const width = value.width || 560;
7775
+ const height = value.height || 315;
7776
+ boxContainer.css({
7777
+ width: `${width}px`,
7778
+ height: `${height}px`,
7779
+ });
7780
+ const videoId = getVideoId(value.url);
7781
+ if (videoId === '') {
7782
+ throw new Error(`Invalid link: ${value.url}`);
7783
+ }
7784
+ // YouTube URL: https://www.youtube.com/watch?v=5sMBhDv4sik
7785
+ // The script for embedding YouTube:
7786
+ // <iframe width="560" height="315" src="https://www.youtube.com/embed/5sMBhDv4sik" title="YouTube video player"
7787
+ // frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7788
+ // referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7789
+ const iframeNode = query(safeTemplate `
7790
+ <iframe width="100%" height="${height}" src="https://www.youtube.com/embed/${videoId}" title="YouTube video player"
7791
+ frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7792
+ referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7793
+ `);
7794
+ const rootNode = boxContainer.find('.lake-video');
7795
+ if (!editor.readonly) {
7796
+ iframeNode.on('load', () => {
7797
+ appendButtonGroup(box);
7798
+ new BoxResizer({
7799
+ root: rootNode,
7800
+ box,
7801
+ width,
7802
+ height,
7803
+ onResize: (newWidth, newHeight) => {
7804
+ iframeNode.attr({
7805
+ height: newHeight.toString(),
7806
+ });
7807
+ },
7808
+ onStop: (newWidth, newHeight) => {
7809
+ box.updateValue({
7810
+ width: newWidth,
7811
+ height: newHeight,
7812
+ });
7813
+ editor.history.save();
7814
+ },
7815
+ }).render();
7816
+ });
7817
+ }
7818
+ rootNode.append(iframeNode);
7819
+ }
7820
+ const videoBox = {
7821
+ type: 'inline',
7822
+ name: 'video',
7823
+ render: box => {
7824
+ const editor = box.getEditor();
7825
+ if (!editor) {
7826
+ return;
7827
+ }
7828
+ const locale = editor.locale;
7829
+ const value = box.value;
7830
+ const boxContainer = box.getContainer();
7831
+ const rootNode = query('<div class="lake-video" />');
7832
+ boxContainer.empty();
7833
+ boxContainer.css({
7834
+ width: '',
7835
+ height: '',
7836
+ });
7837
+ boxContainer.append(rootNode);
7838
+ if (!value.url) {
7839
+ if (editor.readonly) {
7840
+ box.node.hide();
7841
+ return;
7842
+ }
7843
+ const formNode = query(safeTemplate `
7844
+ <div class="lake-video-form">
7845
+ <div class="lake-row lake-desc-row">${locale.video.description()}</div>
7846
+ <div class="lake-row">${locale.video.url()}</div>
7847
+ <div class="lake-row">
7848
+ <input type="text" name="url" placeholder="https://www.youtube.com/watch?v=..." />
7849
+ </div>
7850
+ <div class="lake-row lake-button-row"></div>
7851
+ </div>
7852
+ `);
7853
+ const button = new Button({
7854
+ root: formNode.find('.lake-button-row'),
7855
+ name: 'embed',
7856
+ type: 'primary',
7857
+ text: locale.video.embed(),
7858
+ onClick: () => {
7859
+ const url = getInputValue(formNode, 'url');
7860
+ if (url.indexOf('https://www.youtube.com/') < 0 || getVideoId(url) === '') {
7861
+ editor.config.onMessage('error', locale.video.urlError());
7862
+ return;
7863
+ }
7864
+ box.updateValue('url', url);
7865
+ editor.history.save();
7866
+ formNode.remove();
7867
+ showVideo(box);
7868
+ },
7869
+ });
7870
+ formNode.find('input[name="url"]').on('keydown', createKeybindingsHandler({
7871
+ 'Enter': event => {
7872
+ event.preventDefault();
7873
+ button.node.emit('click');
7874
+ },
7875
+ }));
7876
+ button.render();
7877
+ rootNode.append(formNode);
7878
+ appendButtonGroup(box);
7879
+ }
7880
+ else {
7881
+ showVideo(box);
7882
+ }
7861
7883
  },
7862
7884
  };
7863
7885
 
@@ -7886,7 +7908,7 @@ const boxToolbarItems = [
7886
7908
  },
7887
7909
  },
7888
7910
  ];
7889
- function appendContent(fileNode, box) {
7911
+ function appendContent(rootNode, box) {
7890
7912
  return __awaiter(this, void 0, void 0, function* () {
7891
7913
  const editor = box.getEditor();
7892
7914
  if (!editor) {
@@ -7919,7 +7941,7 @@ function appendContent(fileNode, box) {
7919
7941
  typeNode.append(fileIcon);
7920
7942
  }
7921
7943
  }
7922
- fileNode.append(infoNode);
7944
+ rootNode.append(infoNode);
7923
7945
  });
7924
7946
  }
7925
7947
  const fileBox = {
@@ -7936,26 +7958,45 @@ const fileBox = {
7936
7958
  return;
7937
7959
  }
7938
7960
  const container = box.getContainer();
7939
- const fileNode = query('<div class="lake-file" />');
7940
- fileNode.addClass(`lake-file-${value.status}`);
7941
- appendContent(fileNode, box);
7961
+ const rootNode = query('<div class="lake-file" />');
7962
+ rootNode.addClass(`lake-file-${value.status}`);
7963
+ appendContent(rootNode, box);
7942
7964
  container.empty();
7943
- container.append(fileNode);
7965
+ container.append(rootNode);
7944
7966
  if (!editor.readonly) {
7945
- fileNode.on('click', () => {
7967
+ rootNode.on('click', () => {
7946
7968
  editor.selection.selectBox(box);
7947
7969
  });
7948
7970
  const items = value.status === 'done' ? boxToolbarItems : boxToolbarItems.filter(item => item.name === 'remove');
7949
7971
  box.setToolbar(items);
7950
7972
  }
7951
7973
  else {
7952
- fileNode.on('click', () => {
7974
+ rootNode.on('click', () => {
7953
7975
  window.open(value.url);
7954
7976
  });
7955
7977
  }
7956
7978
  },
7957
7979
  };
7958
7980
 
7981
+ const emojiBox = {
7982
+ type: 'inline',
7983
+ name: 'emoji',
7984
+ render: box => {
7985
+ const editor = box.getEditor();
7986
+ if (!editor) {
7987
+ return;
7988
+ }
7989
+ const value = box.value;
7990
+ const rootNode = query(safeTemplate `
7991
+ <div class="lake-emoji"><img src="${value.url}" title="${value.title}" /></div>
7992
+ `);
7993
+ box.getContainer().append(rootNode);
7994
+ rootNode.on('click', () => {
7995
+ editor.selection.selectBox(box);
7996
+ });
7997
+ },
7998
+ };
7999
+
7959
8000
  var copy = (editor) => {
7960
8001
  editor.event.on('copy', event => {
7961
8002
  const range = editor.selection.range;
@@ -9249,21 +9290,6 @@ var hr = (editor) => {
9249
9290
  });
9250
9291
  };
9251
9292
 
9252
- var video = (editor) => {
9253
- if (editor.readonly) {
9254
- return;
9255
- }
9256
- editor.command.add('video', {
9257
- execute: (value) => {
9258
- const box = editor.selection.insertBox('video', value);
9259
- editor.history.save();
9260
- if (box) {
9261
- box.getContainer().find('input[name="url"]').focus();
9262
- }
9263
- },
9264
- });
9265
- };
9266
-
9267
9293
  const langList = [
9268
9294
  'text',
9269
9295
  'c',
@@ -9299,8 +9325,7 @@ var codeBlock = (editor) => {
9299
9325
  execute: (value) => {
9300
9326
  const box = editor.selection.insertBox('codeBlock', value);
9301
9327
  editor.history.save();
9302
- const codeEditor = box.getData('codeEditor');
9303
- codeEditor.focus();
9328
+ box.getContainer().find('.lake-code-block').emit('click');
9304
9329
  },
9305
9330
  });
9306
9331
  };
@@ -9339,6 +9364,21 @@ var image = (editor) => {
9339
9364
  });
9340
9365
  };
9341
9366
 
9367
+ var video = (editor) => {
9368
+ if (editor.readonly) {
9369
+ return;
9370
+ }
9371
+ editor.command.add('video', {
9372
+ execute: (value) => {
9373
+ const box = editor.selection.insertBox('video', value);
9374
+ editor.history.save();
9375
+ if (box) {
9376
+ box.getContainer().find('input[name="url"]').focus();
9377
+ }
9378
+ },
9379
+ });
9380
+ };
9381
+
9342
9382
  var file = (editor) => {
9343
9383
  editor.setPluginConfig('file', {
9344
9384
  requestMethod: 'POST',
@@ -9372,6 +9412,18 @@ var file = (editor) => {
9372
9412
  });
9373
9413
  };
9374
9414
 
9415
+ var emoji = (editor) => {
9416
+ if (editor.readonly) {
9417
+ return;
9418
+ }
9419
+ editor.command.add('emoji', {
9420
+ execute: (value) => {
9421
+ editor.selection.insertBox('emoji', value);
9422
+ editor.history.save();
9423
+ },
9424
+ });
9425
+ };
9426
+
9375
9427
  const headingTypeMap = new Map([
9376
9428
  ['#', 'h1'],
9377
9429
  ['##', 'h2'],
@@ -9714,7 +9766,7 @@ function splitBlock(editor, block) {
9714
9766
  block.find('li').attr('value', 'false');
9715
9767
  }
9716
9768
  }
9717
- function addBlockOrSplitBlockForBox(editor) {
9769
+ function addOrSplitBlockForBox(editor) {
9718
9770
  const range = editor.selection.range;
9719
9771
  const boxNode = range.startNode.closest('lake-box');
9720
9772
  const block = boxNode.closestBlock();
@@ -9758,7 +9810,7 @@ var enterKey = (editor) => {
9758
9810
  event.preventDefault();
9759
9811
  editor.fixContent();
9760
9812
  if (range.isBox) {
9761
- addBlockOrSplitBlockForBox(editor);
9813
+ addOrSplitBlockForBox(editor);
9762
9814
  editor.history.save();
9763
9815
  return;
9764
9816
  }
@@ -9767,7 +9819,7 @@ var enterKey = (editor) => {
9767
9819
  return;
9768
9820
  }
9769
9821
  if (range.isBox) {
9770
- addBlockOrSplitBlockForBox(editor);
9822
+ addOrSplitBlockForBox(editor);
9771
9823
  editor.history.save();
9772
9824
  return;
9773
9825
  }
@@ -9793,7 +9845,9 @@ function addLineBreak(editor) {
9793
9845
  const prevNode = range.getPrevNode();
9794
9846
  const endText = range.getEndText();
9795
9847
  if (prevNode.name !== 'br' && endText === '') {
9796
- editor.selection.insertContents('<br /><br />');
9848
+ const fragment = new Fragment();
9849
+ fragment.append('<br /><br />');
9850
+ editor.selection.insertFragment(fragment);
9797
9851
  editor.history.save();
9798
9852
  return;
9799
9853
  }
@@ -10142,13 +10196,14 @@ var tabKey = (editor) => {
10142
10196
  }
10143
10197
  event.preventDefault();
10144
10198
  const blocks = editor.selection.range.getBlocks();
10145
- blocks.forEach(block => {
10199
+ for (const block of blocks) {
10146
10200
  if (block.name !== 'p' || block.css('text-indent') === '2em') {
10147
10201
  setBlockIndent(block, 'increase');
10148
- return;
10149
10202
  }
10150
- block.css('text-indent', '2em');
10151
- });
10203
+ else {
10204
+ block.css('text-indent', '2em');
10205
+ }
10206
+ }
10152
10207
  editor.history.save();
10153
10208
  });
10154
10209
  };
@@ -10299,10 +10354,11 @@ var escapeKey = (editor) => {
10299
10354
  };
10300
10355
 
10301
10356
  Editor.box.add(hrBox);
10302
- Editor.box.add(videoBox);
10303
10357
  Editor.box.add(codeBlockBox);
10304
10358
  Editor.box.add(imageBox);
10359
+ Editor.box.add(videoBox);
10305
10360
  Editor.box.add(fileBox);
10361
+ Editor.box.add(emojiBox);
10306
10362
  Editor.plugin.add(copy);
10307
10363
  Editor.plugin.add(cut);
10308
10364
  Editor.plugin.add(paste);
@@ -10330,10 +10386,11 @@ Editor.plugin.add(removeFormat);
10330
10386
  Editor.plugin.add(formatPainter);
10331
10387
  Editor.plugin.add(link);
10332
10388
  Editor.plugin.add(hr);
10333
- Editor.plugin.add(video);
10334
10389
  Editor.plugin.add(codeBlock);
10335
10390
  Editor.plugin.add(image);
10391
+ Editor.plugin.add(video);
10336
10392
  Editor.plugin.add(file);
10393
+ Editor.plugin.add(emoji);
10337
10394
  Editor.plugin.add(markdown);
10338
10395
  Editor.plugin.add(enterKey);
10339
10396
  Editor.plugin.add(shiftEnterKey);
@@ -10343,5 +10400,5 @@ Editor.plugin.add(tabKey);
10343
10400
  Editor.plugin.add(arrowKeys);
10344
10401
  Editor.plugin.add(escapeKey);
10345
10402
 
10346
- export { Box, Button, Dropdown, Editor, Fragment, HTMLParser, Nodes, Range, TextParser, Toolbar, index as Utils, addMark, deleteContents, fixList, icons, insertBookmark, insertContents, insertFragment, insertLink, insertNode, removeMark, setBlocks, splitBlock$1 as splitBlock, splitMarks, toBookmark };
10403
+ 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 };
10347
10404
  //# sourceMappingURL=lake.js.map