lakelib 0.1.13 → 0.1.15

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
@@ -135,17 +135,19 @@ var unlink = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32
135
135
 
136
136
  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
137
 
138
+ 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>";
139
+
138
140
  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
141
 
140
142
  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
143
 
142
144
  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
145
 
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>";
146
+ 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
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,11 @@ 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],
224
227
  ['table', table],
225
228
  ]);
226
229
 
@@ -269,9 +272,10 @@ function safeTemplate(strings, ...keys) {
269
272
  function camelCase(value) {
270
273
  const valueList = value.split('-');
271
274
  let camelString = '';
272
- valueList.forEach((val, index) => {
273
- camelString += (index > 0) ? val.charAt(0).toUpperCase() + val.substring(1) : val;
274
- });
275
+ for (let i = 0; i < valueList.length; i++) {
276
+ const val = valueList[i];
277
+ camelString += (i > 0) ? val.charAt(0).toUpperCase() + val.substring(1) : val;
278
+ }
275
279
  return camelString;
276
280
  }
277
281
 
@@ -876,15 +880,16 @@ class Nodes {
876
880
  var _a;
877
881
  const elementId = element.lakeId;
878
882
  const eventItems = (_a = eventData[elementId]) !== null && _a !== void 0 ? _a : [];
879
- eventItems.forEach((item, index) => {
883
+ for (let i = 0; i < eventItems.length; i++) {
884
+ const item = eventItems[i];
880
885
  if (!type || type === item.type && (!listener || listener === item.listener)) {
881
886
  element.removeEventListener(item.type, item.listener, false);
882
- eventItems[index] = {
887
+ eventItems[i] = {
883
888
  type: '',
884
889
  listener: () => { },
885
890
  };
886
891
  }
887
- });
892
+ }
888
893
  eventData[elementId] = eventItems.filter((item) => item.type !== '');
889
894
  });
890
895
  }
@@ -893,11 +898,11 @@ class Nodes {
893
898
  return this.eachElement(element => {
894
899
  const elementId = element.lakeId;
895
900
  const eventItems = eventData[elementId];
896
- eventItems.forEach(item => {
901
+ for (const item of eventItems) {
897
902
  if (item.type === type) {
898
903
  item.listener(event !== null && event !== void 0 ? event : new Event(type));
899
904
  }
900
- });
905
+ }
901
906
  });
902
907
  }
903
908
  // Gets all event listeners attached to the Nodes object.
@@ -953,9 +958,9 @@ class Nodes {
953
958
  }
954
959
  addClass(className) {
955
960
  if (Array.isArray(className)) {
956
- className.forEach(name => {
961
+ for (const name of className) {
957
962
  this.addClass(name);
958
- });
963
+ }
959
964
  return this;
960
965
  }
961
966
  return this.eachElement(element => {
@@ -967,9 +972,9 @@ class Nodes {
967
972
  }
968
973
  removeClass(className) {
969
974
  if (Array.isArray(className)) {
970
- className.forEach(name => {
975
+ for (const name of className) {
971
976
  this.removeClass(name);
972
- });
977
+ }
973
978
  return this;
974
979
  }
975
980
  return this.eachElement(element => {
@@ -1061,22 +1066,21 @@ class Nodes {
1061
1066
  this.html('');
1062
1067
  return this;
1063
1068
  }
1064
- // Inserts the specified content as the first child of each element.
1069
+ // Inserts the specified content to the beginning of the first element.
1065
1070
  prepend(content) {
1071
+ const element = this.get(0);
1066
1072
  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
- });
1073
+ const list = toNodeList(content).reverse();
1074
+ for (const node of list) {
1075
+ if (element.firstChild) {
1076
+ element.insertBefore(node, element.firstChild);
1077
+ }
1078
+ else {
1079
+ element.appendChild(node);
1080
+ }
1081
+ }
1082
+ return this;
1078
1083
  }
1079
- const element = this.get(0);
1080
1084
  if (content instanceof Nodes) {
1081
1085
  content = content.get(0);
1082
1086
  }
@@ -1088,71 +1092,62 @@ class Nodes {
1088
1092
  }
1089
1093
  return this;
1090
1094
  }
1091
- // Inserts the specified content as the last child of each element.
1095
+ // Inserts the specified content to the end of the first element.
1092
1096
  append(content) {
1097
+ const element = this.get(0);
1093
1098
  if (typeof content === 'string') {
1094
- return this.eachElement(element => {
1095
- const list = toNodeList(content);
1096
- list.forEach((node) => {
1097
- element.appendChild(node);
1098
- });
1099
- });
1099
+ const list = toNodeList(content);
1100
+ for (const node of list) {
1101
+ element.appendChild(node);
1102
+ }
1103
+ return this;
1100
1104
  }
1101
- const element = this.get(0);
1102
1105
  if (content instanceof Nodes) {
1103
1106
  content = content.get(0);
1104
1107
  }
1105
1108
  element.appendChild(content);
1106
1109
  return this;
1107
1110
  }
1108
- // Inserts the specified content before each node.
1111
+ // Inserts the specified content before the first node.
1109
1112
  before(content) {
1113
+ const node = this.get(0);
1114
+ if (!node.parentNode) {
1115
+ return this;
1116
+ }
1110
1117
  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
- });
1118
+ const list = toNodeList(content);
1119
+ for (const target of list) {
1120
+ node.parentNode.insertBefore(target, node);
1121
+ }
1122
+ return this;
1120
1123
  }
1121
- const node = this.get(0);
1122
1124
  if (content instanceof Nodes) {
1123
1125
  content = content.get(0);
1124
1126
  }
1125
- if (!node.parentNode) {
1126
- return this;
1127
- }
1128
1127
  node.parentNode.insertBefore(content, node);
1129
1128
  return this;
1130
1129
  }
1131
- // Inserts the specified content after each node.
1130
+ // Inserts the specified content after the first node.
1132
1131
  after(content) {
1132
+ const node = this.get(0);
1133
+ if (!node.parentNode) {
1134
+ return this;
1135
+ }
1133
1136
  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
- });
1137
+ const list = toNodeList(content).reverse();
1138
+ for (const target of list) {
1139
+ if (node.nextSibling) {
1140
+ node.parentNode.insertBefore(target, node.nextSibling);
1141
+ }
1142
+ else {
1143
+ node.parentNode.appendChild(target);
1144
+ }
1145
+ }
1146
+ return this;
1148
1147
  }
1149
- const node = this.get(0);
1150
1148
  if (content instanceof Nodes) {
1151
1149
  content = content.get(0);
1152
1150
  }
1153
- if (!node.parentNode) {
1154
- return this;
1155
- }
1156
1151
  if (node.nextSibling) {
1157
1152
  node.parentNode.insertBefore(content, node.nextSibling);
1158
1153
  }
@@ -1161,21 +1156,21 @@ class Nodes {
1161
1156
  }
1162
1157
  return this;
1163
1158
  }
1164
- // Replaces each node with the provided new content.
1159
+ // Replaces the first node with the provided new content.
1165
1160
  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
- });
1161
+ const node = this.get(0);
1162
+ if (!node.parentNode) {
1163
+ return this;
1164
+ }
1165
+ let target;
1166
+ if (newContent instanceof Nodes) {
1167
+ target = newContent.get(0);
1168
+ }
1169
+ else {
1170
+ target = toNodeList(newContent)[0];
1171
+ }
1172
+ node.parentNode.replaceChild(target, node);
1173
+ return this;
1179
1174
  }
1180
1175
  // Removes each node from the DOM.
1181
1176
  // keepChildren parameter:
@@ -1470,7 +1465,7 @@ class Range {
1470
1465
  selectBox(boxNode) {
1471
1466
  const boxContainer = boxNode.find('.lake-box-container');
1472
1467
  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.`);
1468
+ throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1474
1469
  }
1475
1470
  this.setStart(boxContainer, 0);
1476
1471
  this.collapseToStart();
@@ -1479,7 +1474,7 @@ class Range {
1479
1474
  selectBoxStart(boxNode) {
1480
1475
  const boxStrip = boxNode.find('.lake-box-strip');
1481
1476
  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.`);
1477
+ throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1483
1478
  }
1484
1479
  this.selectNodeContents(boxStrip.eq(0));
1485
1480
  this.collapseToStart();
@@ -1488,7 +1483,7 @@ class Range {
1488
1483
  selectBoxEnd(boxNode) {
1489
1484
  const boxStrip = boxNode.find('.lake-box-strip');
1490
1485
  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.`);
1486
+ throw new Error(`The box cannot be selected because the box "${boxNode.attr('name')}" (id=${boxNode.id}) has not been rendered yet.`);
1492
1487
  }
1493
1488
  this.selectNodeContents(boxStrip.eq(1));
1494
1489
  this.collapseToStart();
@@ -2020,19 +2015,24 @@ function wrapNodeList(nodeList, wrapper) {
2020
2015
  wrapper = wrapper.clone(true);
2021
2016
  const deepestElement = getDeepest(wrapper);
2022
2017
  nodeList[0].before(wrapper);
2023
- nodeList.forEach(node => {
2018
+ for (const node of nodeList) {
2024
2019
  deepestElement.append(node);
2025
- });
2020
+ }
2026
2021
  return wrapper;
2027
2022
  }
2028
2023
 
2029
2024
  // Removes Zero-width spaces that are dependent on some other text nodes.
2030
2025
  function removeZWS(node) {
2031
2026
  for (const child of node.getWalker()) {
2032
- if (child.isText && child.text().length > 1) {
2033
- const nodeValue = child.text();
2034
- if (/\u200B/.test(child.text())) {
2035
- child.get(0).nodeValue = nodeValue.replace(/\u200B/g, '');
2027
+ if (child.isText) {
2028
+ const text = child.text();
2029
+ if (text === '') {
2030
+ child.remove();
2031
+ }
2032
+ else if (text.length > 1) {
2033
+ if (/\u200B/.test(text)) {
2034
+ child.get(0).nodeValue = text.replace(/\u200B/g, '');
2035
+ }
2036
2036
  }
2037
2037
  }
2038
2038
  }
@@ -2850,6 +2850,7 @@ var enUS = {
2850
2850
  highlight: 'Highlight',
2851
2851
  image: 'Image',
2852
2852
  file: 'File',
2853
+ emoji: 'Emoji',
2853
2854
  removeColor: 'Remove color',
2854
2855
  },
2855
2856
  link: {
@@ -2933,6 +2934,7 @@ var zhCN = {
2933
2934
  highlight: '文字背景',
2934
2935
  image: '图片',
2935
2936
  file: '文件',
2937
+ emoji: '表情',
2936
2938
  removeColor: '默认',
2937
2939
  },
2938
2940
  link: {
@@ -3016,6 +3018,7 @@ var ja = {
3016
3018
  highlight: '文字の背景',
3017
3019
  image: '画像',
3018
3020
  file: 'ファイル',
3021
+ emoji: '絵文字',
3019
3022
  removeColor: 'デフォルト',
3020
3023
  },
3021
3024
  link: {
@@ -3099,6 +3102,7 @@ var ko = {
3099
3102
  highlight: '글자 배경',
3100
3103
  image: '이미지',
3101
3104
  file: '파일',
3105
+ emoji: '이모지',
3102
3106
  removeColor: '기본색',
3103
3107
  },
3104
3108
  link: {
@@ -3279,7 +3283,7 @@ class Dropdown {
3279
3283
  }
3280
3284
  });
3281
3285
  menuNode.css('visibility', 'hidden');
3282
- menuNode.show(config.menuType === 'color' ? 'flex' : 'block');
3286
+ menuNode.show(config.menuType === 'list' ? 'block' : 'flex');
3283
3287
  const dropdownNativeNode = dropdownNode.get(0);
3284
3288
  const dropdownRect = dropdownNativeNode.getBoundingClientRect();
3285
3289
  // A overflow width on the left side, greater than 0 indicates an overflow.
@@ -3342,7 +3346,7 @@ class Dropdown {
3342
3346
  if (dropdownNode.attr('disabled')) {
3343
3347
  return;
3344
3348
  }
3345
- const value = dropdownNode.attr('color') || config.defaultValue;
3349
+ const value = dropdownNode.attr('color') || config.defaultValue || '';
3346
3350
  config.onSelect(value);
3347
3351
  });
3348
3352
  }
@@ -3369,14 +3373,17 @@ class Dropdown {
3369
3373
  });
3370
3374
  }
3371
3375
  render() {
3372
- var _a;
3376
+ var _a, _b;
3373
3377
  const config = this.config;
3378
+ const defaultValue = (_a = config.defaultValue) !== null && _a !== void 0 ? _a : '';
3374
3379
  const dropdownNode = this.node;
3375
3380
  const titleNode = dropdownNode.find('.lake-dropdown-title');
3376
3381
  if (!config.downIcon) {
3377
3382
  titleNode.addClass('lake-dropdown-title-no-down');
3378
3383
  }
3379
- titleNode.css('width', config.width);
3384
+ if (config.width) {
3385
+ titleNode.css('width', config.width);
3386
+ }
3380
3387
  const tooltip = typeof config.tooltip === 'string' ? config.tooltip : config.tooltip(this.locale);
3381
3388
  titleNode.attr('title', tooltip);
3382
3389
  const textNode = titleNode.find('.lake-dropdown-text');
@@ -3393,13 +3400,16 @@ class Dropdown {
3393
3400
  }
3394
3401
  const menuNode = query('<ul class="lake-dropdown-menu" />');
3395
3402
  menuNode.addClass(`lake-${config.menuType}-dropdown-menu`);
3396
- Dropdown.setValue(dropdownNode, [config.defaultValue]);
3403
+ if (config.menuWidth) {
3404
+ menuNode.css('width', config.menuWidth);
3405
+ }
3406
+ Dropdown.setValue(dropdownNode, [defaultValue]);
3397
3407
  if (textNode.length > 0) {
3398
3408
  const menuMap = Dropdown.getMenuMap(config.menuItems, this.locale);
3399
- textNode.text((_a = menuMap.get(config.defaultValue)) !== null && _a !== void 0 ? _a : config.defaultValue);
3409
+ textNode.text((_b = menuMap.get(defaultValue)) !== null && _b !== void 0 ? _b : defaultValue);
3400
3410
  }
3401
3411
  if (config.menuType === 'color') {
3402
- this.updateColorAccent(titleNode, config.defaultValue);
3412
+ this.updateColorAccent(titleNode, defaultValue);
3403
3413
  }
3404
3414
  this.apppendMenuItems(menuNode);
3405
3415
  dropdownNode.append(titleNode);
@@ -3484,21 +3494,19 @@ class BoxToolbar {
3484
3494
  // Renders a toolbar for the specified box.
3485
3495
  render() {
3486
3496
  this.root.append(this.container);
3487
- this.items.forEach(item => {
3497
+ for (const item of this.items) {
3488
3498
  if (item === '|') {
3489
3499
  this.appendDivider();
3490
- return;
3491
3500
  }
3492
- if (item.type === 'button') {
3501
+ else if (item.type === 'button') {
3493
3502
  this.buttonItemList.push(item);
3494
3503
  this.appendButton(item);
3495
- return;
3496
3504
  }
3497
- if (item.type === 'dropdown') {
3505
+ else if (item.type === 'dropdown') {
3498
3506
  this.dropdownItemList.push(item);
3499
3507
  this.appendDropdown(item);
3500
3508
  }
3501
- });
3509
+ }
3502
3510
  this.updatePosition();
3503
3511
  }
3504
3512
  unmount() {
@@ -3506,8 +3514,6 @@ class BoxToolbar {
3506
3514
  }
3507
3515
  }
3508
3516
 
3509
- // A key-value object for storing data about box.
3510
- const boxData = {};
3511
3517
  const framework = safeTemplate `
3512
3518
  <span class="lake-box-strip"><br /></span>
3513
3519
  <div class="lake-box-container" contenteditable="false"></div>
@@ -3519,7 +3525,7 @@ class Box {
3519
3525
  if (typeof node === 'string') {
3520
3526
  const component = boxes.get(node);
3521
3527
  if (component === undefined) {
3522
- throw new Error(`Box '${node}' has not been defined yet.`);
3528
+ throw new Error(`Box "${node}" has not been defined yet.`);
3523
3529
  }
3524
3530
  const type = encode(component.type);
3525
3531
  const name = encode(component.name);
@@ -3532,15 +3538,12 @@ class Box {
3532
3538
  this.node = query(node);
3533
3539
  const component = boxes.get(this.name);
3534
3540
  if (component === undefined) {
3535
- throw new Error(`Box '${this.name}' has not been defined yet.`);
3541
+ throw new Error(`Box "${this.name}" has not been defined yet.`);
3536
3542
  }
3537
3543
  if (component.value && !this.node.hasAttr('value')) {
3538
3544
  this.value = component.value;
3539
3545
  }
3540
3546
  }
3541
- if (!boxData[this.node.id]) {
3542
- boxData[this.node.id] = {};
3543
- }
3544
3547
  }
3545
3548
  // Adds the framework of the box.
3546
3549
  addFramework() {
@@ -3566,7 +3569,7 @@ class Box {
3566
3569
  container.removeClass('lake-box-hovered');
3567
3570
  });
3568
3571
  container.on('click', () => {
3569
- debug(`Box '${this.name}' (id = ${this.node.id}) value:`);
3572
+ debug(`Box "${this.name}" (id = ${this.node.id}) value:`);
3570
3573
  debug(this.value);
3571
3574
  });
3572
3575
  if (this.type === 'block' && this.node.isContentEditable) {
@@ -3605,14 +3608,6 @@ class Box {
3605
3608
  }
3606
3609
  this.value = value;
3607
3610
  }
3608
- // Returns data of the box.
3609
- getData(key) {
3610
- return boxData[this.node.id][key];
3611
- }
3612
- // Updates data of the box.
3613
- setData(key, value) {
3614
- boxData[this.node.id][key] = value;
3615
- }
3616
3611
  // Returns the editor instance of the box.
3617
3612
  getEditor() {
3618
3613
  const container = this.node.closest('div[contenteditable]');
@@ -3667,16 +3662,15 @@ class Box {
3667
3662
  newContainer.append(content);
3668
3663
  morph(container, newContainer);
3669
3664
  }
3670
- debug(`Box '${this.name}' (id: ${this.node.id}) rendered`);
3665
+ debug(`Box "${this.name}" (id: ${this.node.id}) rendered`);
3671
3666
  }
3672
3667
  // Destroys a rendered box.
3673
3668
  unmount() {
3674
3669
  this.event.emit('blur');
3675
3670
  this.event.emit('beforeunmount');
3676
- boxData[this.node.id] = {};
3677
3671
  this.event.removeAllListeners();
3678
3672
  this.node.empty();
3679
- debug(`Box '${this.name}' (id: ${this.node.id}) unmounted`);
3673
+ debug(`Box "${this.name}" (id: ${this.node.id}) unmounted`);
3680
3674
  }
3681
3675
  // Returns a HTML string of the box.
3682
3676
  getHTML() {
@@ -3847,6 +3841,71 @@ function request(option) {
3847
3841
  return xhr;
3848
3842
  }
3849
3843
 
3844
+ function uploadFile(config) {
3845
+ const { editor, name, file, onError, onSuccess } = config;
3846
+ const { requestMethod, requestAction, requestTypes } = editor.config[name];
3847
+ if (requestTypes.indexOf(file.type) < 0) {
3848
+ if (onError) {
3849
+ onError(`File "${file.name}" is not allowed for uploading.`);
3850
+ }
3851
+ throw new Error(`Cannot upload file "${file.name}" because its type "${file.type}" is not found in ['${requestTypes.join('\', \'')}'].`);
3852
+ }
3853
+ const box = editor.selection.insertBox(name, {
3854
+ url: URL.createObjectURL(file),
3855
+ status: 'uploading',
3856
+ name: file.name,
3857
+ size: file.size,
3858
+ type: file.type,
3859
+ lastModified: file.lastModified,
3860
+ });
3861
+ let xhr = request({
3862
+ onProgress: e => {
3863
+ const percentNode = box.node.find('.lake-percent');
3864
+ const percent = Math.round(e.percent);
3865
+ percentNode.text(`${percent < 100 ? percent : 99} %`);
3866
+ },
3867
+ onError: (error, body) => {
3868
+ xhr = null;
3869
+ debug(error.toString(), body);
3870
+ box.updateValue('status', 'error');
3871
+ box.render();
3872
+ if (onError) {
3873
+ onError(error.toString());
3874
+ }
3875
+ },
3876
+ onSuccess: body => {
3877
+ xhr = null;
3878
+ if (!body.url) {
3879
+ box.updateValue('status', 'error');
3880
+ box.render();
3881
+ if (onError) {
3882
+ onError('Cannot find the url field.');
3883
+ }
3884
+ return;
3885
+ }
3886
+ box.updateValue({
3887
+ status: 'done',
3888
+ url: body.url,
3889
+ });
3890
+ box.render();
3891
+ editor.history.save();
3892
+ if (onSuccess) {
3893
+ onSuccess();
3894
+ }
3895
+ },
3896
+ file,
3897
+ action: requestAction,
3898
+ method: requestMethod,
3899
+ });
3900
+ box.event.on('beforeunmount', () => {
3901
+ if (xhr) {
3902
+ xhr.abort();
3903
+ debug('Upload canceled');
3904
+ }
3905
+ });
3906
+ return box;
3907
+ }
3908
+
3850
3909
  // String
3851
3910
 
3852
3911
  var index = /*#__PURE__*/Object.freeze({
@@ -3879,6 +3938,7 @@ var index = /*#__PURE__*/Object.freeze({
3879
3938
  template: template,
3880
3939
  toHex: toHex,
3881
3940
  toNodeList: toNodeList,
3941
+ uploadFile: uploadFile,
3882
3942
  wrapNodeList: wrapNodeList
3883
3943
  });
3884
3944
 
@@ -3886,6 +3946,10 @@ class Fragment {
3886
3946
  constructor(fragment) {
3887
3947
  this.fragment = fragment !== null && fragment !== void 0 ? fragment : document.createDocumentFragment();
3888
3948
  }
3949
+ // Gets a native fragment.
3950
+ get() {
3951
+ return this.fragment;
3952
+ }
3889
3953
  // Returns the descendants of the fragment which are selected by the specified CSS selector.
3890
3954
  find(selector) {
3891
3955
  const nodeList = [];
@@ -3905,7 +3969,7 @@ class Fragment {
3905
3969
  }
3906
3970
  // Inserts the specified node as the last child.
3907
3971
  append(node) {
3908
- node.each(nativeNode => {
3972
+ query(node).each(nativeNode => {
3909
3973
  this.fragment.appendChild(nativeNode);
3910
3974
  });
3911
3975
  }
@@ -4287,9 +4351,8 @@ function insertBookmark(range) {
4287
4351
  function removeAndNormalizeNode(node, range) {
4288
4352
  const previousNode = node.prev();
4289
4353
  const nextNode = node.next();
4290
- if (previousNode.isText && nextNode.isText) {
4354
+ if (previousNode.isText || nextNode.isText) {
4291
4355
  const parentNode = node.parent();
4292
- removeZWS(parentNode);
4293
4356
  node.remove();
4294
4357
  parentNode.get(0).normalize();
4295
4358
  }
@@ -4407,6 +4470,9 @@ function deleteContents(range) {
4407
4470
 
4408
4471
  // Inserts a DocumentFragment object into the specified range.
4409
4472
  function insertFragment(range, fragment) {
4473
+ if (fragment instanceof Fragment) {
4474
+ fragment = fragment.get();
4475
+ }
4410
4476
  if (range.commonAncestor.isOutside) {
4411
4477
  return;
4412
4478
  }
@@ -4424,16 +4490,6 @@ function insertFragment(range, fragment) {
4424
4490
  range.adjustBlock();
4425
4491
  }
4426
4492
 
4427
- // Inserts a HTML string into the specified range.
4428
- function insertContents(range, value) {
4429
- const nodes = query(value);
4430
- const fragment = document.createDocumentFragment();
4431
- nodes.each(nativeNode => {
4432
- fragment.appendChild(nativeNode);
4433
- });
4434
- insertFragment(range, fragment);
4435
- }
4436
-
4437
4493
  function getTopNonBlockNodes(range) {
4438
4494
  const container = range.commonAncestor.closest('div[contenteditable="true"],td');
4439
4495
  let nodeList = [];
@@ -4495,7 +4551,7 @@ function setBlocks(range, value) {
4495
4551
  }
4496
4552
  else {
4497
4553
  const block = valueNode.clone(true);
4498
- if (node.isList && node.attr('indent') !== '') {
4554
+ if (block.isList && node.isList && node.attr('indent') !== '') {
4499
4555
  block.attr('indent', node.attr('indent'));
4500
4556
  }
4501
4557
  const deepestBlock = getDeepest(block);
@@ -4593,7 +4649,7 @@ function splitBlock$1(range) {
4593
4649
  }
4594
4650
 
4595
4651
  // Removes empty marks that contain no content.
4596
- function removeEmptyMarks$1(node) {
4652
+ function removeEmptyMarks$2(node) {
4597
4653
  if (node.isMark && node.isEmpty) {
4598
4654
  node.remove();
4599
4655
  return;
@@ -4615,8 +4671,8 @@ function splitMarksAtPoint(node, offset, removeEmptyMark) {
4615
4671
  const parts = splitNodes(node, offset, limitBlock);
4616
4672
  if (parts) {
4617
4673
  if (removeEmptyMark) {
4618
- removeEmptyMarks$1(parts.start);
4619
- removeEmptyMarks$1(parts.end);
4674
+ removeEmptyMarks$2(parts.start);
4675
+ removeEmptyMarks$2(parts.end);
4620
4676
  if (!parts.start.isEmpty) {
4621
4677
  start = parts.start;
4622
4678
  }
@@ -4802,7 +4858,7 @@ function addMark(range, value) {
4802
4858
  }
4803
4859
 
4804
4860
  // Removes empty marks that contain no content.
4805
- function removeEmptyMarks(node) {
4861
+ function removeEmptyMarks$1(node) {
4806
4862
  if (node.isMark && node.isEmpty) {
4807
4863
  node.remove();
4808
4864
  return;
@@ -4860,13 +4916,13 @@ function removeMark(range, value) {
4860
4916
  return;
4861
4917
  }
4862
4918
  if (parts.end) {
4863
- removeEmptyMarks(parts.end);
4919
+ removeEmptyMarks$1(parts.end);
4864
4920
  }
4865
4921
  const zeroWidthSpace = new Nodes(document.createTextNode('\u200B'));
4866
4922
  const newMark = copyNestedMarks(parts.start, tagName);
4867
4923
  if (!newMark) {
4868
4924
  parts.start.after(zeroWidthSpace);
4869
- removeEmptyMarks(parts.start);
4925
+ removeEmptyMarks$1(parts.start);
4870
4926
  if (zeroWidthSpace.prev().isText) {
4871
4927
  range.setStartAfter(zeroWidthSpace.prev());
4872
4928
  range.collapseToStart();
@@ -4879,7 +4935,7 @@ function removeMark(range, value) {
4879
4935
  }
4880
4936
  appendDeepest(newMark, zeroWidthSpace);
4881
4937
  parts.start.after(newMark);
4882
- removeEmptyMarks(parts.start);
4938
+ removeEmptyMarks$1(parts.start);
4883
4939
  range.shrinkAfter(newMark);
4884
4940
  return;
4885
4941
  }
@@ -4947,8 +5003,6 @@ function insertLink(range, value) {
4947
5003
  return linkNode;
4948
5004
  }
4949
5005
 
4950
- var version = "0.1.13";
4951
-
4952
5006
  // Inserts a box into the specified range.
4953
5007
  function insertBox(range, boxName, boxValue) {
4954
5008
  if (range.commonAncestor.isOutside) {
@@ -5026,6 +5080,8 @@ function removeBox(range) {
5026
5080
  return box;
5027
5081
  }
5028
5082
 
5083
+ var version = "0.1.15";
5084
+
5029
5085
  // Returns the attributes of the element as an key-value object.
5030
5086
  function getAttributes(node) {
5031
5087
  const nativeNode = node.get(0);
@@ -5116,7 +5172,10 @@ class Selection {
5116
5172
  // Updates the saved range with the range of the native selection.
5117
5173
  updateByRange() {
5118
5174
  const newRange = this.getRangeFromNativeSelection();
5119
- if (this.range.get() === newRange.get()) {
5175
+ if (this.range.startNode.get(0) === newRange.startNode.get(0) &&
5176
+ this.range.startOffset === newRange.startOffset &&
5177
+ this.range.endNode.get(0) === newRange.endNode.get(0) &&
5178
+ this.range.endOffset === newRange.endOffset) {
5120
5179
  return;
5121
5180
  }
5122
5181
  this.range = newRange;
@@ -5160,9 +5219,6 @@ class Selection {
5160
5219
  insertFragment(fragment) {
5161
5220
  return insertFragment(this.range, fragment);
5162
5221
  }
5163
- insertContents(value) {
5164
- return insertContents(this.range, value);
5165
- }
5166
5222
  deleteContents() {
5167
5223
  return deleteContents(this.range);
5168
5224
  }
@@ -5202,7 +5258,7 @@ class Selection {
5202
5258
  insertBox(boxName, boxValue) {
5203
5259
  const box = insertBox(this.range, boxName, boxValue);
5204
5260
  if (!box) {
5205
- throw new Error(`Box '${boxName}' cannot be inserted outside the editor.`);
5261
+ throw new Error(`Box "${boxName}" cannot be inserted outside the editor.`);
5206
5262
  }
5207
5263
  return box;
5208
5264
  }
@@ -5218,7 +5274,6 @@ class Selection {
5218
5274
  class Command {
5219
5275
  constructor(selection) {
5220
5276
  this.commandMap = new Map();
5221
- this.event = new EventEmitter();
5222
5277
  this.selection = selection;
5223
5278
  }
5224
5279
  add(name, commandItem) {
@@ -5236,7 +5291,7 @@ class Command {
5236
5291
  getItem(name) {
5237
5292
  const commandItem = this.commandMap.get(name);
5238
5293
  if (commandItem === undefined) {
5239
- throw new Error(`Command '${name}' does not exist`);
5294
+ throw new Error(`Command "${name}" has not been defined yet.`);
5240
5295
  }
5241
5296
  return commandItem;
5242
5297
  }
@@ -5266,10 +5321,8 @@ class Command {
5266
5321
  }
5267
5322
  execute(name, ...data) {
5268
5323
  const commandItem = this.getItem(name);
5269
- this.event.emit('beforeexecute', name);
5270
5324
  commandItem.execute.apply(this, data);
5271
- this.event.emit('execute', name);
5272
- debug(`Command '${name}' executed`);
5325
+ debug(`Command "${name}" executed`);
5273
5326
  }
5274
5327
  }
5275
5328
 
@@ -5287,11 +5340,11 @@ class Command {
5287
5340
  // inputs 'e': value: 'abe', list: ['a', 'ab', 'abe'], index: 3, canRedo: false
5288
5341
  class History {
5289
5342
  constructor(selection) {
5343
+ this.canSave = true;
5290
5344
  // an array for storing the history items
5291
5345
  this.list = [];
5292
5346
  // the next index of the list
5293
5347
  this.index = 0;
5294
- this.canSave = true;
5295
5348
  this.limit = 100;
5296
5349
  this.event = new EventEmitter();
5297
5350
  this.selection = selection;
@@ -5343,6 +5396,12 @@ class History {
5343
5396
  this.removeIdfromBoxes(container);
5344
5397
  this.removeIdfromBoxes(otherContainer);
5345
5398
  }
5399
+ get canUndo() {
5400
+ return this.index > 1 && !!this.list[this.index - 1];
5401
+ }
5402
+ get canRedo() {
5403
+ return !!this.list[this.index];
5404
+ }
5346
5405
  cloneContainer() {
5347
5406
  const range = this.selection.range;
5348
5407
  const newContainer = this.container.clone(true);
@@ -5354,6 +5413,12 @@ class History {
5354
5413
  return newContainer;
5355
5414
  }
5356
5415
  if (range.isInsideBox) {
5416
+ const boxNode = range.commonAncestor.closest('lake-box');
5417
+ const boxNodePath = boxNode.path();
5418
+ const newBoxNode = newContainer.find(boxNodePath);
5419
+ const newRange = range.clone();
5420
+ newRange.selectBox(newBoxNode);
5421
+ insertBookmark(newRange);
5357
5422
  return newContainer;
5358
5423
  }
5359
5424
  const startNodePath = range.startNode.path();
@@ -5366,23 +5431,14 @@ class History {
5366
5431
  insertBookmark(newRange);
5367
5432
  return newContainer;
5368
5433
  }
5369
- get count() {
5370
- return this.list.length;
5371
- }
5372
- get canUndo() {
5373
- return this.index > 1 && !!this.list[this.index - 1];
5374
- }
5375
- get canRedo() {
5376
- return !!this.list[this.index];
5377
- }
5378
5434
  undo() {
5379
- if (!this.list[this.index - 1]) {
5435
+ if (!this.list[this.index - 2]) {
5380
5436
  return;
5381
5437
  }
5382
5438
  this.selection.insertBookmark();
5383
5439
  const value = this.getValue(this.container);
5384
- while (this.index > 0) {
5385
- const prevItem = this.list[this.index - 1];
5440
+ while (this.index > 1) {
5441
+ const prevItem = this.list[this.index - 2];
5386
5442
  if (!prevItem) {
5387
5443
  break;
5388
5444
  }
@@ -5394,9 +5450,6 @@ class History {
5394
5450
  break;
5395
5451
  }
5396
5452
  }
5397
- if (this.index < 1) {
5398
- this.index = 1;
5399
- }
5400
5453
  this.selection.updateByBookmark();
5401
5454
  debug(`History undone (index: ${this.index})`);
5402
5455
  }
@@ -5428,7 +5481,11 @@ class History {
5428
5481
  pause() {
5429
5482
  this.canSave = false;
5430
5483
  }
5431
- save(emitSaveEvent = true) {
5484
+ save(options = {}) {
5485
+ var _a, _b, _c;
5486
+ const inputType = (_a = options.inputType) !== null && _a !== void 0 ? _a : '';
5487
+ const update = (_b = options.update) !== null && _b !== void 0 ? _b : false;
5488
+ const emitEvent = (_c = options.emitEvent) !== null && _c !== void 0 ? _c : true;
5432
5489
  if (!this.canSave) {
5433
5490
  return;
5434
5491
  }
@@ -5438,16 +5495,25 @@ class History {
5438
5495
  this.removeBookmark(this.getValue(this.list[this.index - 1])) === this.removeBookmark(value)) {
5439
5496
  return;
5440
5497
  }
5441
- this.list.splice(this.index, Infinity, item);
5442
- this.index++;
5498
+ if (update) {
5499
+ this.list.splice(this.index - 1, Infinity, item);
5500
+ }
5501
+ else {
5502
+ this.list.splice(this.index, Infinity, item);
5503
+ this.index++;
5504
+ }
5443
5505
  if (this.list.length > this.limit) {
5444
5506
  this.list.shift();
5445
5507
  this.index = this.list.length;
5446
5508
  }
5447
- if (emitSaveEvent) {
5448
- this.event.emit('save', denormalizeValue(value));
5509
+ debug(`History saved (index: ${this.index}, inputType: "${inputType}", update: ${update}, emitEvent: ${emitEvent})`);
5510
+ if (emitEvent) {
5511
+ this.event.emit('save', denormalizeValue(value), {
5512
+ inputType,
5513
+ update,
5514
+ emitEvent,
5515
+ });
5449
5516
  }
5450
- debug(`History saved (index: ${this.index})`);
5451
5517
  }
5452
5518
  }
5453
5519
 
@@ -5536,9 +5602,9 @@ class Plugin {
5536
5602
  this.pluginList.push(plugin);
5537
5603
  }
5538
5604
  loadAll(editor) {
5539
- this.pluginList.forEach(plugin => {
5605
+ for (const plugin of this.pluginList) {
5540
5606
  plugin(editor);
5541
- });
5607
+ }
5542
5608
  }
5543
5609
  }
5544
5610
 
@@ -5551,6 +5617,7 @@ const defaultConfig = {
5551
5617
  indentWithTab: true,
5552
5618
  lang: 'en-US',
5553
5619
  minChangeSize: 5,
5620
+ historySize: 100,
5554
5621
  onMessage: (type, message) => {
5555
5622
  if (type === 'success') {
5556
5623
  // eslint-disable-next-line no-console
@@ -5571,6 +5638,7 @@ const defaultConfig = {
5571
5638
  class Editor {
5572
5639
  constructor(config) {
5573
5640
  this.unsavedInputData = '';
5641
+ this.unsavedInputCount = 0;
5574
5642
  this.state = {
5575
5643
  appliedItems: [],
5576
5644
  disabledNameMap: new Map(),
@@ -5601,9 +5669,6 @@ class Editor {
5601
5669
  }
5602
5670
  this.event.emit('paste', event);
5603
5671
  };
5604
- this.beforeunloadListener = () => {
5605
- this.history.save();
5606
- };
5607
5672
  this.selectionchangeListener = () => {
5608
5673
  this.selection.updateByRange();
5609
5674
  this.updateBoxSelectionStyle();
@@ -5619,6 +5684,7 @@ class Editor {
5619
5684
  this.resizeListener = () => {
5620
5685
  this.event.emit('resize');
5621
5686
  };
5687
+ // Updates the classes of all boxes when the current selection of the editor is changed.
5622
5688
  this.updateBoxSelectionStyle = debounce(() => {
5623
5689
  // The editor has been unmounted.
5624
5690
  if (this.root.first().length === 0) {
@@ -5663,12 +5729,12 @@ class Editor {
5663
5729
  boxContainer.removeClass('lake-box-selected');
5664
5730
  box.event.emit('blur');
5665
5731
  });
5666
- this.event.emit('boxselectionstylechange');
5667
5732
  }, 50, {
5668
5733
  leading: false,
5669
5734
  trailing: true,
5670
5735
  maxWait: 50,
5671
5736
  });
5737
+ // Triggers the statechange event when the current selection of the editor is changed.
5672
5738
  this.emitStateChangeEvent = debounce(() => {
5673
5739
  const commandNames = this.command.getNames();
5674
5740
  let appliedItems = this.selection.getAppliedItems();
@@ -5715,13 +5781,6 @@ class Editor {
5715
5781
  trailing: true,
5716
5782
  maxWait: 100,
5717
5783
  });
5718
- this.emitChangeEvent = (value) => {
5719
- this.fixContent();
5720
- this.emitStateChangeEvent();
5721
- this.togglePlaceholderClass(value);
5722
- this.scrollToCaret();
5723
- this.event.emit('change', value);
5724
- };
5725
5784
  if (!config.root) {
5726
5785
  throw new Error('The root of the config must be specified.');
5727
5786
  }
@@ -5748,9 +5807,11 @@ class Editor {
5748
5807
  this.selection = new Selection(this.container);
5749
5808
  this.command = new Command(this.selection);
5750
5809
  this.history = new History(this.selection);
5810
+ this.history.limit = this.config.historySize;
5751
5811
  this.keystroke = new Keystroke(this.container);
5752
5812
  editors.set(this.container.id, this);
5753
5813
  }
5814
+ // Adds or Removes a placeholder class.
5754
5815
  togglePlaceholderClass(value) {
5755
5816
  value = denormalizeValue(value);
5756
5817
  const className = 'lake-show-placeholder';
@@ -5761,7 +5822,8 @@ class Editor {
5761
5822
  this.container.removeClass(className);
5762
5823
  }
5763
5824
  }
5764
- inputInBoxStrip() {
5825
+ // Moves the input text from box strip to normal position.
5826
+ moveBoxStripText() {
5765
5827
  const selection = this.selection;
5766
5828
  const range = selection.range;
5767
5829
  const stripNode = range.startNode.closest('.lake-box-strip');
@@ -5791,6 +5853,12 @@ class Editor {
5791
5853
  stripNode.html('<br />');
5792
5854
  selection.insertNode(document.createTextNode(text));
5793
5855
  }
5856
+ // Resets the value of unsaved input property.
5857
+ resetUnsavedInputData() {
5858
+ this.unsavedInputData = '';
5859
+ this.unsavedInputCount = 0;
5860
+ }
5861
+ // Binds events about input.
5794
5862
  bindInputEvents() {
5795
5863
  this.container.on('compositionstart', () => {
5796
5864
  this.isComposing = true;
@@ -5798,19 +5866,6 @@ class Editor {
5798
5866
  this.container.on('compositionend', () => {
5799
5867
  this.isComposing = false;
5800
5868
  });
5801
- this.container.on('beforeinput', event => {
5802
- const inputEvent = event;
5803
- const range = this.selection.range;
5804
- if (range.isBoxStart || range.isBoxEnd) {
5805
- this.commitUnsavedInputData();
5806
- return;
5807
- }
5808
- if (inputEvent.inputType === 'insertText' ||
5809
- inputEvent.inputType === 'insertCompositionText') {
5810
- return;
5811
- }
5812
- this.commitUnsavedInputData();
5813
- });
5814
5869
  this.container.on('input', event => {
5815
5870
  const inputEvent = event;
5816
5871
  // Here setTimeout is necessary because isComposing is not false after ending composition.
@@ -5826,7 +5881,7 @@ class Editor {
5826
5881
  return;
5827
5882
  }
5828
5883
  if (range.isBoxStart || range.isBoxEnd) {
5829
- this.inputInBoxStrip();
5884
+ this.moveBoxStripText();
5830
5885
  this.history.save();
5831
5886
  this.event.emit('input', inputEvent);
5832
5887
  return;
@@ -5834,30 +5889,60 @@ class Editor {
5834
5889
  if (inputEvent.inputType === 'insertText' ||
5835
5890
  inputEvent.inputType === 'insertCompositionText') {
5836
5891
  this.unsavedInputData += (_a = inputEvent.data) !== null && _a !== void 0 ? _a : '';
5892
+ this.unsavedInputCount++;
5837
5893
  if (this.unsavedInputData.length < this.config.minChangeSize) {
5838
- this.event.emit('input', inputEvent);
5839
- this.emitChangeEvent(this.getValue());
5840
- return;
5894
+ this.history.save({
5895
+ inputType: 'insertText',
5896
+ update: this.unsavedInputCount > 1,
5897
+ });
5898
+ }
5899
+ else {
5900
+ this.history.save({
5901
+ inputType: 'insertText',
5902
+ update: true,
5903
+ });
5904
+ this.resetUnsavedInputData();
5841
5905
  }
5906
+ this.event.emit('input', inputEvent);
5907
+ return;
5842
5908
  }
5843
5909
  this.history.save();
5844
5910
  this.event.emit('input', inputEvent);
5845
5911
  }, 0);
5846
5912
  });
5847
- this.command.event.on('beforeexecute', () => this.commitUnsavedInputData());
5848
5913
  }
5914
+ // Binds events about history.
5849
5915
  bindHistoryEvents() {
5916
+ const executeCommonMethods = (value) => {
5917
+ if (this.fixContent()) {
5918
+ this.history.save({
5919
+ update: true,
5920
+ emitEvent: false,
5921
+ });
5922
+ value = this.getValue();
5923
+ }
5924
+ this.emitStateChangeEvent();
5925
+ this.togglePlaceholderClass(value);
5926
+ this.scrollToCaret();
5927
+ this.event.emit('change', value);
5928
+ };
5850
5929
  this.history.event.on('undo', value => {
5851
5930
  this.renderBoxes();
5852
- this.emitChangeEvent(value);
5931
+ executeCommonMethods(value);
5932
+ this.resetUnsavedInputData();
5853
5933
  });
5854
5934
  this.history.event.on('redo', value => {
5855
5935
  this.renderBoxes();
5856
- this.emitChangeEvent(value);
5936
+ executeCommonMethods(value);
5937
+ this.resetUnsavedInputData();
5857
5938
  });
5858
- this.history.event.on('save', value => {
5939
+ this.history.event.on('save', (value, options) => {
5859
5940
  this.removeBoxGarbage();
5860
- this.emitChangeEvent(value);
5941
+ executeCommonMethods(value);
5942
+ this.selection.sync();
5943
+ if (options.inputType !== 'insertText') {
5944
+ this.resetUnsavedInputData();
5945
+ }
5861
5946
  });
5862
5947
  }
5863
5948
  // Returns a boolean value indicating whether the editor has focus.
@@ -5874,47 +5959,34 @@ class Editor {
5874
5959
  }
5875
5960
  // Fixes wrong content, especially empty tag.
5876
5961
  fixContent() {
5962
+ let changed = false;
5877
5963
  let children = this.container.children();
5878
5964
  for (const child of children) {
5879
5965
  if ((child.isBlock || child.isMark) && child.html() === '') {
5880
5966
  child.remove();
5881
- debug('fixContent(): empty tag was removed');
5967
+ changed = true;
5968
+ debug(`Content fixed: empty tag "${child.name}" was removed`);
5882
5969
  }
5883
5970
  }
5884
5971
  children = this.container.children();
5885
5972
  if (children.length === 0) {
5886
5973
  this.container.html('<p><br /></p>');
5887
5974
  this.selection.range.shrinkAfter(this.container);
5888
- debug('fixContent(): default paragraph was added');
5889
- return;
5975
+ changed = true;
5976
+ debug('Content fixed: default paragraph was added');
5890
5977
  }
5891
- if (children.length === 1) {
5978
+ else if (children.length === 1) {
5892
5979
  const child = children[0];
5893
5980
  if (child.isVoid) {
5894
5981
  const paragraph = query('<p />');
5895
5982
  child.before(paragraph);
5896
5983
  paragraph.append(child);
5897
5984
  this.selection.range.shrinkAfter(paragraph);
5898
- debug('fixContent(): void element was wrapped in paragraph');
5985
+ changed = true;
5986
+ debug(`Content fixed: void element "${child.name}" was wrapped in paragraph`);
5899
5987
  }
5900
5988
  }
5901
- }
5902
- // Saves the input data which is unsaved.
5903
- commitUnsavedInputData() {
5904
- if (this.unsavedInputData.length > 0) {
5905
- this.history.save(false);
5906
- this.unsavedInputData = '';
5907
- }
5908
- }
5909
- // Updates some state before custom modifications.
5910
- prepareOperation() {
5911
- this.commitUnsavedInputData();
5912
- this.history.pause();
5913
- }
5914
- // Saves custom modifications to the history.
5915
- commitOperation() {
5916
- this.history.continue();
5917
- this.history.save();
5989
+ return changed;
5918
5990
  }
5919
5991
  // Sets default config for a plugin.
5920
5992
  setPluginConfig(name, config) {
@@ -5951,11 +6023,11 @@ class Editor {
5951
6023
  box.render();
5952
6024
  });
5953
6025
  }
5954
- // Sets focus on the editor area.
6026
+ // Sets focus on the editor.
5955
6027
  focus() {
5956
6028
  this.container.focus();
5957
6029
  }
5958
- // Removes focus from the editor area.
6030
+ // Removes focus from the editor.
5959
6031
  blur() {
5960
6032
  this.container.blur();
5961
6033
  }
@@ -5963,6 +6035,9 @@ class Editor {
5963
6035
  scrollToCaret() {
5964
6036
  // Creates an artificial caret that is the same size as the caret at the current caret position.
5965
6037
  const rangeRect = this.selection.range.getRect();
6038
+ if (rangeRect.x === 0 || rangeRect.y === 0) {
6039
+ return;
6040
+ }
5966
6041
  const containerRect = this.container.get(0).getBoundingClientRect();
5967
6042
  const artificialCaret = query('<div class="lake-artificial-caret" />');
5968
6043
  const left = rangeRect.x - containerRect.x;
@@ -5991,7 +6066,7 @@ class Editor {
5991
6066
  }
5992
6067
  artificialCaret.remove();
5993
6068
  }
5994
- // Sets the specified HTML string to the editor area.
6069
+ // Sets the specified value to the editor.
5995
6070
  setValue(value) {
5996
6071
  value = normalizeValue(value);
5997
6072
  const htmlParser = new HTMLParser(value);
@@ -6002,15 +6077,14 @@ class Editor {
6002
6077
  this.renderBoxes();
6003
6078
  this.selection.updateByBookmark();
6004
6079
  }
6005
- // Returns the contents from the editor.
6080
+ // Returns the value of the editor.
6006
6081
  getValue() {
6007
- const bookmark = this.selection.insertBookmark();
6008
- let value = new HTMLParser(this.container).getHTML();
6082
+ const item = this.history.cloneContainer();
6083
+ let value = new HTMLParser(item).getHTML();
6009
6084
  value = denormalizeValue(value);
6010
- this.selection.toBookmark(bookmark);
6011
6085
  return value;
6012
6086
  }
6013
- // Renders an editor area and set default value to it.
6087
+ // Renders an editor area and sets default value to it.
6014
6088
  render() {
6015
6089
  const value = normalizeValue(this.config.value);
6016
6090
  const htmlParser = new HTMLParser(value);
@@ -6025,7 +6099,9 @@ class Editor {
6025
6099
  Editor.plugin.loadAll(this);
6026
6100
  if (!this.readonly) {
6027
6101
  this.selection.updateByBookmark();
6028
- this.history.save();
6102
+ this.history.save({
6103
+ emitEvent: false,
6104
+ });
6029
6105
  }
6030
6106
  this.renderBoxes();
6031
6107
  if (this.toolbar) {
@@ -6035,7 +6111,6 @@ class Editor {
6035
6111
  if (!this.readonly) {
6036
6112
  document.addEventListener('cut', this.cutListener);
6037
6113
  document.addEventListener('paste', this.pasteListener);
6038
- window.addEventListener('beforeunload', this.beforeunloadListener);
6039
6114
  document.addEventListener('selectionchange', this.selectionchangeListener);
6040
6115
  document.addEventListener('click', this.clickListener);
6041
6116
  window.addEventListener('resize', this.resizeListener);
@@ -6043,10 +6118,9 @@ class Editor {
6043
6118
  this.bindHistoryEvents();
6044
6119
  }
6045
6120
  }
6046
- // Destroys a rendered editor.
6121
+ // Destroys the rendered editor.
6047
6122
  unmount() {
6048
6123
  this.event.removeAllListeners();
6049
- this.command.event.removeAllListeners();
6050
6124
  this.history.event.removeAllListeners();
6051
6125
  this.root.empty();
6052
6126
  this.popupContainer.remove();
@@ -6054,7 +6128,6 @@ class Editor {
6054
6128
  if (!this.readonly) {
6055
6129
  document.removeEventListener('cut', this.cutListener);
6056
6130
  document.removeEventListener('paste', this.pasteListener);
6057
- window.removeEventListener('beforeunload', this.beforeunloadListener);
6058
6131
  document.removeEventListener('selectionchange', this.selectionchangeListener);
6059
6132
  document.removeEventListener('click', this.clickListener);
6060
6133
  window.removeEventListener('resize', this.resizeListener);
@@ -6258,7 +6331,7 @@ const moreStyleMenuItems = [
6258
6331
  text: locale => locale.toolbar.code(),
6259
6332
  },
6260
6333
  ];
6261
- // These colors are from Ant Design (https://ant.design/docs/spec/colors)
6334
+ // These colors are sourced from Ant Design (https://ant.design/docs/spec/colors)
6262
6335
  const colors = [
6263
6336
  // Dust Red, Volcano, Sunset Orange, Calendula Gold, Sunrise Yellow, Lime, Polar Green, Cyan, Daybreak Blue, Geek Blue, Golden Purple, Magenta
6264
6337
  '#f5222d', '#fa541c', '#fa8c16', '#faad14', '#fadb14', '#a0d911', '#52c41a', '#13c2c2', '#1677ff', '#2f54eb', '#722ed1', '#eb2f96', // color 6
@@ -6533,19 +6606,19 @@ const toolbarItems = [
6533
6606
  },
6534
6607
  },
6535
6608
  {
6536
- name: 'video',
6609
+ name: 'codeBlock',
6537
6610
  type: 'button',
6538
- icon: icons.get('video'),
6539
- tooltip: locale => locale.toolbar.video(),
6611
+ icon: icons.get('codeBlock'),
6612
+ tooltip: locale => locale.toolbar.codeBlock(),
6540
6613
  onClick: (editor, value) => {
6541
6614
  editor.command.execute(value);
6542
6615
  },
6543
6616
  },
6544
6617
  {
6545
- name: 'codeBlock',
6618
+ name: 'video',
6546
6619
  type: 'button',
6547
- icon: icons.get('codeBlock'),
6548
- tooltip: locale => locale.toolbar.codeBlock(),
6620
+ icon: icons.get('video'),
6621
+ tooltip: locale => locale.toolbar.video(),
6549
6622
  onClick: (editor, value) => {
6550
6623
  editor.command.execute(value);
6551
6624
  },
@@ -6568,9 +6641,7 @@ const toolbarItems = [
6568
6641
  type: 'dropdown',
6569
6642
  downIcon: icons.get('down'),
6570
6643
  icon: icons.get('list'),
6571
- defaultValue: '',
6572
6644
  tooltip: locale => locale.toolbar.list(),
6573
- width: 'auto',
6574
6645
  menuType: 'list',
6575
6646
  menuItems: listMenuItems,
6576
6647
  onSelect: (editor, value) => {
@@ -6582,9 +6653,7 @@ const toolbarItems = [
6582
6653
  type: 'dropdown',
6583
6654
  downIcon: icons.get('down'),
6584
6655
  icon: icons.get('alignLeft'),
6585
- defaultValue: '',
6586
6656
  tooltip: locale => locale.toolbar.align(),
6587
- width: 'auto',
6588
6657
  menuType: 'list',
6589
6658
  menuItems: alignMenuItems,
6590
6659
  onSelect: (editor, value) => {
@@ -6596,9 +6665,7 @@ const toolbarItems = [
6596
6665
  type: 'dropdown',
6597
6666
  downIcon: icons.get('down'),
6598
6667
  icon: icons.get('increaseIndent'),
6599
- defaultValue: '',
6600
6668
  tooltip: locale => locale.toolbar.indent(),
6601
- width: 'auto',
6602
6669
  menuType: 'list',
6603
6670
  menuItems: indentMenuItems,
6604
6671
  onSelect: (editor, value) => {
@@ -6635,9 +6702,7 @@ const toolbarItems = [
6635
6702
  name: 'moreStyle',
6636
6703
  type: 'dropdown',
6637
6704
  icon: icons.get('more'),
6638
- defaultValue: '',
6639
6705
  tooltip: locale => locale.toolbar.moreStyle(),
6640
- width: 'auto',
6641
6706
  menuType: 'list',
6642
6707
  menuItems: moreStyleMenuItems,
6643
6708
  selectedValues: appliedItems => {
@@ -6663,9 +6728,9 @@ const toolbarItems = [
6663
6728
  accentIcon: icons.get('fontColorAccent'),
6664
6729
  defaultValue: '#f5222d',
6665
6730
  tooltip: locale => locale.toolbar.fontColor(),
6666
- width: 'auto',
6667
6731
  menuType: 'color',
6668
6732
  menuItems: colorMenuItems,
6733
+ menuWidth: '296px',
6669
6734
  onSelect: (editor, value) => {
6670
6735
  editor.command.execute('fontColor', value);
6671
6736
  },
@@ -6678,9 +6743,9 @@ const toolbarItems = [
6678
6743
  accentIcon: icons.get('highlightAccent'),
6679
6744
  defaultValue: '#fadb14',
6680
6745
  tooltip: locale => locale.toolbar.highlight(),
6681
- width: 'auto',
6682
6746
  menuType: 'color',
6683
6747
  menuItems: colorMenuItems,
6748
+ menuWidth: '296px',
6684
6749
  onSelect: (editor, value) => {
6685
6750
  editor.command.execute('highlight', value);
6686
6751
  },
@@ -6703,64 +6768,6 @@ const toolbarItems = [
6703
6768
  },
6704
6769
  ];
6705
6770
 
6706
- function uploadFile(config) {
6707
- const { editor, name, file, onError, onSuccess } = config;
6708
- const { requestMethod, requestAction, requestTypes } = editor.config[name];
6709
- if (requestTypes.indexOf(file.type) < 0) {
6710
- if (onError) {
6711
- onError(`File '${file.name}' is not allowed for uploading.`);
6712
- }
6713
- throw new Error(`Cannot upload file '${file.name}' because its type '${file.type}' is not found in ['${requestTypes.join('\', \'')}'].`);
6714
- }
6715
- const box = editor.selection.insertBox(name, {
6716
- url: URL.createObjectURL(file),
6717
- status: 'uploading',
6718
- name: file.name,
6719
- size: file.size,
6720
- type: file.type,
6721
- lastModified: file.lastModified,
6722
- });
6723
- const xhr = request({
6724
- onProgress: e => {
6725
- const percentNode = box.node.find('.lake-percent');
6726
- const percent = Math.round(e.percent);
6727
- percentNode.text(`${percent < 100 ? percent : 99} %`);
6728
- },
6729
- onError: (error, body) => {
6730
- debug(error.toString(), body);
6731
- box.updateValue('status', 'error');
6732
- box.render();
6733
- if (onError) {
6734
- onError(error.toString());
6735
- }
6736
- },
6737
- onSuccess: body => {
6738
- if (!body.url) {
6739
- box.updateValue('status', 'error');
6740
- box.render();
6741
- if (onError) {
6742
- onError('Cannot find the url field.');
6743
- }
6744
- return;
6745
- }
6746
- box.updateValue({
6747
- status: 'done',
6748
- url: body.url,
6749
- });
6750
- box.render();
6751
- editor.history.save();
6752
- if (onSuccess) {
6753
- onSuccess();
6754
- }
6755
- },
6756
- file,
6757
- action: requestAction,
6758
- method: requestMethod,
6759
- });
6760
- box.setData('xhr', xhr);
6761
- return box;
6762
- }
6763
-
6764
6771
  const defaultItems = [
6765
6772
  'undo',
6766
6773
  'redo',
@@ -6783,9 +6790,9 @@ const defaultItems = [
6783
6790
  'hr',
6784
6791
  ];
6785
6792
  const toolbarItemMap = new Map();
6786
- toolbarItems.forEach(item => {
6793
+ for (const item of toolbarItems) {
6787
6794
  toolbarItemMap.set(item.name, item);
6788
- });
6795
+ }
6789
6796
  class Toolbar {
6790
6797
  constructor(config) {
6791
6798
  this.placement = 'top';
@@ -6830,6 +6837,7 @@ class Toolbar {
6830
6837
  width: item.width,
6831
6838
  menuType: item.menuType,
6832
6839
  menuItems: item.menuItems,
6840
+ menuWidth: item.menuWidth,
6833
6841
  tabIndex: -1,
6834
6842
  placement: this.placement === 'top' ? 'bottom' : 'top',
6835
6843
  onSelect: value => {
@@ -6931,7 +6939,7 @@ class Toolbar {
6931
6939
  Dropdown.setValue(dropdownNode, selectedValues);
6932
6940
  const textNode = dropdownNode.find('.lake-dropdown-text');
6933
6941
  if (textNode.length > 0) {
6934
- const key = selectedValues[0] || item.defaultValue;
6942
+ const key = selectedValues[0] || item.defaultValue || '';
6935
6943
  const menuMap = this.allMenuMap.get(item.name);
6936
6944
  const text = (_a = (menuMap && menuMap.get(key))) !== null && _a !== void 0 ? _a : key;
6937
6945
  textNode.text(text);
@@ -6943,36 +6951,35 @@ class Toolbar {
6943
6951
  render(editor) {
6944
6952
  this.root.empty();
6945
6953
  this.root.append(this.container);
6946
- this.items.forEach(name => {
6954
+ for (const name of this.items) {
6947
6955
  if (name === '|') {
6948
6956
  this.appendDivider();
6949
- return;
6950
- }
6951
- let item;
6952
- if (typeof name === 'string') {
6953
- item = toolbarItemMap.get(name);
6954
- if (!item) {
6955
- return;
6956
- }
6957
6957
  }
6958
6958
  else {
6959
- item = name;
6960
- }
6961
- if (item.type === 'button') {
6962
- this.buttonItemList.push(item);
6963
- this.appendButton(editor, item);
6964
- return;
6965
- }
6966
- if (item.type === 'dropdown') {
6967
- this.allMenuMap.set(item.name, Dropdown.getMenuMap(item.menuItems, editor.locale));
6968
- this.dropdownItemList.push(item);
6969
- this.appendDropdown(editor, item);
6970
- return;
6971
- }
6972
- if (item.type === 'upload') {
6973
- this.appendUpload(editor, item);
6959
+ let item;
6960
+ if (typeof name === 'string') {
6961
+ item = toolbarItemMap.get(name);
6962
+ if (!item) {
6963
+ throw new Error(`ToolbarItem "${name}" has not been defined yet.`);
6964
+ }
6965
+ }
6966
+ else {
6967
+ item = name;
6968
+ }
6969
+ if (item.type === 'button') {
6970
+ this.buttonItemList.push(item);
6971
+ this.appendButton(editor, item);
6972
+ }
6973
+ else if (item.type === 'dropdown') {
6974
+ this.allMenuMap.set(item.name, Dropdown.getMenuMap(item.menuItems, editor.locale));
6975
+ this.dropdownItemList.push(item);
6976
+ this.appendDropdown(editor, item);
6977
+ }
6978
+ else if (item.type === 'upload') {
6979
+ this.appendUpload(editor, item);
6980
+ }
6974
6981
  }
6975
- });
6982
+ }
6976
6983
  }
6977
6984
  }
6978
6985
 
@@ -6984,248 +6991,15 @@ const hrBox = {
6984
6991
  if (!editor) {
6985
6992
  return;
6986
6993
  }
6987
- const hrNode = query('<div class="lake-hr"><hr /></div>');
6988
- box.getContainer().append(hrNode);
6989
- hrNode.on('click', () => {
6994
+ const rootNode = query('<div class="lake-hr"><hr /></div>');
6995
+ box.getContainer().append(rootNode);
6996
+ rootNode.on('click', () => {
6990
6997
  editor.selection.selectBox(box);
6991
6998
  });
6992
6999
  },
6993
7000
  html: () => '<hr />',
6994
7001
  };
6995
7002
 
6996
- class BoxResizer {
6997
- constructor(config) {
6998
- this.config = config;
6999
- this.root = config.root;
7000
- this.box = config.box;
7001
- }
7002
- bindEvents(pointerNode) {
7003
- const box = this.box;
7004
- const boxContainer = box.getContainer();
7005
- const resizerNode = pointerNode.closest('.lake-resizer');
7006
- const infoNode = resizerNode.find('.lake-resizer-info');
7007
- const isPlus = pointerNode.attr('class').indexOf('-right') >= 0;
7008
- const initialWidth = boxContainer.width();
7009
- const initialHeight = boxContainer.height();
7010
- const rate = initialHeight / initialWidth;
7011
- let clientX = 0;
7012
- let width = 0;
7013
- // resizing box
7014
- const pointermoveListener = (event) => {
7015
- const pointerEvent = event;
7016
- const diffX = pointerEvent.clientX - clientX;
7017
- const newWidth = Math.round(isPlus ? width + diffX : width - diffX);
7018
- const newHeight = Math.round(rate * newWidth);
7019
- infoNode.text(`${newWidth} x ${newHeight}`);
7020
- boxContainer.css({
7021
- width: `${newWidth}px`,
7022
- height: `${newHeight}px`,
7023
- });
7024
- if (this.config.onResize) {
7025
- this.config.onResize(newWidth, newHeight);
7026
- }
7027
- };
7028
- // start resizing
7029
- const pointerdownListener = (event) => {
7030
- const pointerEvent = event;
7031
- const pointerNativeNode = pointerNode.get(0);
7032
- // The capture will be implicitly released after a pointerup or pointercancel event.
7033
- // https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
7034
- pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
7035
- clientX = pointerEvent.clientX;
7036
- width = boxContainer.width();
7037
- infoNode.show();
7038
- pointerNode.on('pointermove', pointermoveListener);
7039
- };
7040
- // stop resizing
7041
- const pointerupListner = () => {
7042
- pointerNode.off('pointermove');
7043
- infoNode.hide();
7044
- width = box.getContainer().width();
7045
- const height = Math.round(rate * width);
7046
- this.config.onStop(width, height);
7047
- };
7048
- // cancel resizing
7049
- const pointercancelListner = () => {
7050
- pointerNode.off('pointermove');
7051
- infoNode.hide();
7052
- };
7053
- pointerNode.on('pointerdown', pointerdownListener);
7054
- pointerNode.on('pointerup', pointerupListner);
7055
- pointerNode.on('pointercancel', pointercancelListner);
7056
- }
7057
- render() {
7058
- const { width, height } = this.config;
7059
- const resizerNode = query(safeTemplate `
7060
- <div class="lake-resizer">
7061
- <div class="lake-resizer-top-left"></div>
7062
- <div class="lake-resizer-top-right"></div>
7063
- <div class="lake-resizer-bottom-left"></div>
7064
- <div class="lake-resizer-bottom-right"></div>
7065
- <div class="lake-resizer-info">${width} x ${height}</div>
7066
- </div>
7067
- `);
7068
- this.bindEvents(resizerNode.find('.lake-resizer-top-left'));
7069
- this.bindEvents(resizerNode.find('.lake-resizer-top-right'));
7070
- this.bindEvents(resizerNode.find('.lake-resizer-bottom-left'));
7071
- this.bindEvents(resizerNode.find('.lake-resizer-bottom-right'));
7072
- this.root.append(resizerNode);
7073
- }
7074
- }
7075
-
7076
- function getVideoId(url) {
7077
- const result = /\w+$/i.exec(url || '');
7078
- return result ? result[0] : '';
7079
- }
7080
- function getInputValue(videoNode, name) {
7081
- const inputElement = videoNode.find(`input[name="${name}"]`);
7082
- const nativeInputElement = inputElement.get(0);
7083
- return nativeInputElement.value;
7084
- }
7085
- function appendButtonGroup(box) {
7086
- const editor = box.getEditor();
7087
- if (!editor) {
7088
- return;
7089
- }
7090
- const boxContainer = box.getContainer();
7091
- const videoNode = boxContainer.find('.lake-video');
7092
- const buttonGroupNode = query(safeTemplate `
7093
- <div class="lake-button-group">
7094
- <button type="button" tabindex="-1" class="lake-button-remove" title="${editor.locale.video.remove()}"></button>
7095
- </div>
7096
- `);
7097
- const removeButton = buttonGroupNode.find('.lake-button-remove');
7098
- const removeIcon = icons.get('remove');
7099
- if (removeIcon) {
7100
- removeButton.append(removeIcon);
7101
- }
7102
- buttonGroupNode.find('.lake-button-remove').on('click', event => {
7103
- event.stopPropagation();
7104
- editor.selection.removeBox(box);
7105
- editor.history.save();
7106
- editor.selection.sync();
7107
- });
7108
- videoNode.append(buttonGroupNode);
7109
- }
7110
- function showVideo(box) {
7111
- const editor = box.getEditor();
7112
- if (!editor) {
7113
- return;
7114
- }
7115
- const boxContainer = box.getContainer();
7116
- const value = box.value;
7117
- const width = value.width || 560;
7118
- const height = value.height || 315;
7119
- boxContainer.css({
7120
- width: `${width}px`,
7121
- height: `${height}px`,
7122
- });
7123
- const videoId = getVideoId(value.url);
7124
- if (videoId === '') {
7125
- throw new Error(`Invalid link: ${value.url}`);
7126
- }
7127
- // YouTube URL: https://www.youtube.com/watch?v=5sMBhDv4sik
7128
- // The script for embedding YouTube:
7129
- // <iframe width="560" height="315" src="https://www.youtube.com/embed/5sMBhDv4sik" title="YouTube video player"
7130
- // frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7131
- // referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7132
- const iframeNode = query(safeTemplate `
7133
- <iframe width="100%" height="${height}" src="https://www.youtube.com/embed/${videoId}" title="YouTube video player"
7134
- frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7135
- referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7136
- `);
7137
- const videoNode = boxContainer.find('.lake-video');
7138
- if (!editor.readonly) {
7139
- iframeNode.on('load', () => {
7140
- appendButtonGroup(box);
7141
- new BoxResizer({
7142
- root: videoNode,
7143
- box,
7144
- width,
7145
- height,
7146
- onResize: (newWidth, newHeight) => {
7147
- iframeNode.attr({
7148
- height: newHeight.toString(),
7149
- });
7150
- },
7151
- onStop: (newWidth, newHeight) => {
7152
- box.updateValue({
7153
- width: newWidth,
7154
- height: newHeight,
7155
- });
7156
- editor.history.save();
7157
- },
7158
- }).render();
7159
- });
7160
- }
7161
- videoNode.append(iframeNode);
7162
- }
7163
- const videoBox = {
7164
- type: 'inline',
7165
- name: 'video',
7166
- render: box => {
7167
- const editor = box.getEditor();
7168
- if (!editor) {
7169
- return;
7170
- }
7171
- const locale = editor.locale;
7172
- const value = box.value;
7173
- const boxContainer = box.getContainer();
7174
- const videoNode = query('<div class="lake-video" />');
7175
- boxContainer.empty();
7176
- boxContainer.css({
7177
- width: '',
7178
- height: '',
7179
- });
7180
- boxContainer.append(videoNode);
7181
- if (!value.url) {
7182
- if (editor.readonly) {
7183
- box.node.hide();
7184
- return;
7185
- }
7186
- const formNode = query(safeTemplate `
7187
- <div class="lake-video-form">
7188
- <div class="lake-row lake-desc-row">${locale.video.description()}</div>
7189
- <div class="lake-row">${locale.video.url()}</div>
7190
- <div class="lake-row">
7191
- <input type="text" name="url" placeholder="https://www.youtube.com/watch?v=..." />
7192
- </div>
7193
- <div class="lake-row lake-button-row"></div>
7194
- </div>
7195
- `);
7196
- const button = new Button({
7197
- root: formNode.find('.lake-button-row'),
7198
- name: 'embed',
7199
- type: 'primary',
7200
- text: locale.video.embed(),
7201
- onClick: () => {
7202
- const url = getInputValue(formNode, 'url');
7203
- if (url.indexOf('https://www.youtube.com/') < 0 || getVideoId(url) === '') {
7204
- editor.config.onMessage('error', locale.video.urlError());
7205
- return;
7206
- }
7207
- box.updateValue('url', url);
7208
- editor.history.save();
7209
- formNode.remove();
7210
- showVideo(box);
7211
- },
7212
- });
7213
- formNode.find('input[name="url"]').on('keydown', createKeybindingsHandler({
7214
- 'Enter': event => {
7215
- event.preventDefault();
7216
- button.node.emit('click');
7217
- },
7218
- }));
7219
- button.render();
7220
- videoNode.append(formNode);
7221
- appendButtonGroup(box);
7222
- }
7223
- else {
7224
- showVideo(box);
7225
- }
7226
- },
7227
- };
7228
-
7229
7003
  const config = {
7230
7004
  comment: '#57606a',
7231
7005
  name: '#444d56',
@@ -7312,12 +7086,12 @@ const codeBlockBox = {
7312
7086
  if (!editor) {
7313
7087
  return;
7314
7088
  }
7315
- const codeBlockNode = query('<div class="lake-code-block" />');
7089
+ const rootNode = query('<div class="lake-code-block" />');
7316
7090
  const container = box.getContainer();
7317
7091
  container.css('width', `${editor.container.innerWidth() - 2}px`);
7318
7092
  container.empty();
7319
- container.append(codeBlockNode);
7320
- const codeBlockNativeNode = codeBlockNode.get(0);
7093
+ container.append(rootNode);
7094
+ const codeBlockNativeNode = rootNode.get(0);
7321
7095
  if (!codeBlockNativeNode) {
7322
7096
  return;
7323
7097
  }
@@ -7328,12 +7102,12 @@ const codeBlockBox = {
7328
7102
  box.node.hide();
7329
7103
  return;
7330
7104
  }
7331
- codeBlockNode.addClass('lake-code-block-error');
7332
- codeBlockNode.text(`
7105
+ rootNode.addClass('lake-code-block-error');
7106
+ rootNode.text(`
7333
7107
  The code cannot be displayed because window.LakeCodeMirror is not found.
7334
7108
  Please check if the "lake-codemirror" library is added to this page.
7335
7109
  `.trim());
7336
- codeBlockNode.on('click', () => {
7110
+ rootNode.on('click', () => {
7337
7111
  editor.selection.selectBox(box);
7338
7112
  });
7339
7113
  return;
@@ -7383,14 +7157,13 @@ const codeBlockBox = {
7383
7157
  updateListener,
7384
7158
  ],
7385
7159
  });
7386
- codeBlockNode.find('[contenteditable="true"]').attr('tabindex', '-1');
7160
+ rootNode.find('[contenteditable="true"]').attr('tabindex', '-1');
7387
7161
  const dropdown = new Dropdown({
7388
- root: codeBlockNode,
7162
+ root: rootNode,
7389
7163
  name: 'langType',
7390
7164
  downIcon: icons.get('down'),
7391
7165
  defaultValue: langItem ? boxValue.lang : codeBlockConfig.defaultLang,
7392
7166
  tooltip: editor.locale.codeBlock.langType(),
7393
- width: 'auto',
7394
7167
  menuType: 'list',
7395
7168
  menuItems: langItems.map((item) => ({
7396
7169
  value: item.value,
@@ -7408,11 +7181,16 @@ const codeBlockBox = {
7408
7181
  },
7409
7182
  });
7410
7183
  dropdown.render();
7411
- box.setData('codeEditor', codeEditor);
7412
7184
  const resizeListener = () => {
7413
7185
  container.css('width', `${editor.container.innerWidth() - 2}px`);
7414
7186
  };
7415
7187
  editor.event.on('resize', resizeListener);
7188
+ rootNode.on('click', () => {
7189
+ if (codeEditor.hasFocus) {
7190
+ return;
7191
+ }
7192
+ codeEditor.focus();
7193
+ });
7416
7194
  box.event.on('beforeunmount', () => {
7417
7195
  codeEditor.destroy();
7418
7196
  editor.event.off('resize', resizeListener);
@@ -7453,6 +7231,86 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
7453
7231
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
7454
7232
  };
7455
7233
 
7234
+ class BoxResizer {
7235
+ constructor(config) {
7236
+ this.config = config;
7237
+ this.root = config.root;
7238
+ this.box = config.box;
7239
+ }
7240
+ bindEvents(pointerNode) {
7241
+ const box = this.box;
7242
+ const boxContainer = box.getContainer();
7243
+ const resizerNode = pointerNode.closest('.lake-resizer');
7244
+ const infoNode = resizerNode.find('.lake-resizer-info');
7245
+ const isPlus = pointerNode.attr('class').indexOf('-right') >= 0;
7246
+ const initialWidth = boxContainer.width();
7247
+ const initialHeight = boxContainer.height();
7248
+ const rate = initialHeight / initialWidth;
7249
+ let clientX = 0;
7250
+ let width = 0;
7251
+ // resizing box
7252
+ const pointermoveListener = (event) => {
7253
+ const pointerEvent = event;
7254
+ const diffX = pointerEvent.clientX - clientX;
7255
+ const newWidth = Math.round(isPlus ? width + diffX : width - diffX);
7256
+ const newHeight = Math.round(rate * newWidth);
7257
+ infoNode.text(`${newWidth} x ${newHeight}`);
7258
+ boxContainer.css({
7259
+ width: `${newWidth}px`,
7260
+ height: `${newHeight}px`,
7261
+ });
7262
+ if (this.config.onResize) {
7263
+ this.config.onResize(newWidth, newHeight);
7264
+ }
7265
+ };
7266
+ // start resizing
7267
+ const pointerdownListener = (event) => {
7268
+ const pointerEvent = event;
7269
+ const pointerNativeNode = pointerNode.get(0);
7270
+ // The capture will be implicitly released after a pointerup or pointercancel event.
7271
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
7272
+ pointerNativeNode.setPointerCapture(pointerEvent.pointerId);
7273
+ clientX = pointerEvent.clientX;
7274
+ width = boxContainer.width();
7275
+ infoNode.show();
7276
+ pointerNode.on('pointermove', pointermoveListener);
7277
+ };
7278
+ // stop resizing
7279
+ const pointerupListner = () => {
7280
+ pointerNode.off('pointermove');
7281
+ infoNode.hide();
7282
+ width = box.getContainer().width();
7283
+ const height = Math.round(rate * width);
7284
+ this.config.onStop(width, height);
7285
+ };
7286
+ // cancel resizing
7287
+ const pointercancelListner = () => {
7288
+ pointerNode.off('pointermove');
7289
+ infoNode.hide();
7290
+ };
7291
+ pointerNode.on('pointerdown', pointerdownListener);
7292
+ pointerNode.on('pointerup', pointerupListner);
7293
+ pointerNode.on('pointercancel', pointercancelListner);
7294
+ }
7295
+ render() {
7296
+ const { width, height } = this.config;
7297
+ const resizerNode = query(safeTemplate `
7298
+ <div class="lake-resizer">
7299
+ <div class="lake-resizer-top-left"></div>
7300
+ <div class="lake-resizer-top-right"></div>
7301
+ <div class="lake-resizer-bottom-left"></div>
7302
+ <div class="lake-resizer-bottom-right"></div>
7303
+ <div class="lake-resizer-info">${width} x ${height}</div>
7304
+ </div>
7305
+ `);
7306
+ this.bindEvents(resizerNode.find('.lake-resizer-top-left'));
7307
+ this.bindEvents(resizerNode.find('.lake-resizer-top-right'));
7308
+ this.bindEvents(resizerNode.find('.lake-resizer-bottom-left'));
7309
+ this.bindEvents(resizerNode.find('.lake-resizer-bottom-right'));
7310
+ this.root.append(resizerNode);
7311
+ }
7312
+ }
7313
+
7456
7314
  // Loads an image and get its width and height.
7457
7315
  function getImageInfo(url) {
7458
7316
  return __awaiter(this, void 0, void 0, function* () {
@@ -7590,7 +7448,7 @@ function openFullScreen(box) {
7590
7448
  lightbox.loadAndOpen(currentIndex);
7591
7449
  }
7592
7450
  // Displays error icon and filename.
7593
- function renderError(imageNode, box) {
7451
+ function renderError(rootNode, box) {
7594
7452
  return __awaiter(this, void 0, void 0, function* () {
7595
7453
  const editor = box.getEditor();
7596
7454
  if (!editor) {
@@ -7621,12 +7479,12 @@ function renderError(imageNode, box) {
7621
7479
  if (imageIcon) {
7622
7480
  errorNode.find('.lake-error-icon').append(imageIcon);
7623
7481
  }
7624
- imageNode.append(buttonGroupNode);
7625
- imageNode.append(errorNode);
7482
+ rootNode.append(buttonGroupNode);
7483
+ rootNode.append(errorNode);
7626
7484
  });
7627
7485
  }
7628
7486
  // Displays an image with uplaoding progress.
7629
- function renderUploading(imageNode, box) {
7487
+ function renderUploading(rootNode, box) {
7630
7488
  return __awaiter(this, void 0, void 0, function* () {
7631
7489
  const editor = box.getEditor();
7632
7490
  if (!editor) {
@@ -7635,7 +7493,7 @@ function renderUploading(imageNode, box) {
7635
7493
  const value = box.value;
7636
7494
  const imageInfo = yield getImageInfo(value.url);
7637
7495
  if (!imageInfo.width || !imageInfo.height) {
7638
- yield renderError(imageNode, box);
7496
+ yield renderError(rootNode, box);
7639
7497
  return;
7640
7498
  }
7641
7499
  const maxWidth = editor.container.innerWidth() - 2;
@@ -7680,13 +7538,13 @@ function renderUploading(imageNode, box) {
7680
7538
  draggable: 'false',
7681
7539
  alt: value.name,
7682
7540
  });
7683
- imageNode.append(buttonGroupNode);
7684
- imageNode.append(progressNode);
7685
- imageNode.append(imgNode);
7541
+ rootNode.append(buttonGroupNode);
7542
+ rootNode.append(progressNode);
7543
+ rootNode.append(imgNode);
7686
7544
  });
7687
7545
  }
7688
7546
  // Displays an image that can be previewed or removed.
7689
- function renderDone(imageNode, box) {
7547
+ function renderDone(rootNode, box) {
7690
7548
  return __awaiter(this, void 0, void 0, function* () {
7691
7549
  const editor = box.getEditor();
7692
7550
  if (!editor) {
@@ -7699,7 +7557,7 @@ function renderDone(imageNode, box) {
7699
7557
  return;
7700
7558
  }
7701
7559
  if (!imageInfo.width || !imageInfo.height) {
7702
- yield renderError(imageNode, box);
7560
+ yield renderError(rootNode, box);
7703
7561
  return;
7704
7562
  }
7705
7563
  let width = value.width;
@@ -7742,9 +7600,9 @@ function renderDone(imageNode, box) {
7742
7600
  draggable: 'false',
7743
7601
  alt: value.name,
7744
7602
  });
7745
- imageNode.append(buttonGroupNode);
7603
+ rootNode.append(buttonGroupNode);
7746
7604
  new BoxResizer({
7747
- root: imageNode,
7605
+ root: rootNode,
7748
7606
  box,
7749
7607
  width,
7750
7608
  height,
@@ -7756,7 +7614,7 @@ function renderDone(imageNode, box) {
7756
7614
  editor.history.save();
7757
7615
  },
7758
7616
  }).render();
7759
- imageNode.append(imgNode);
7617
+ rootNode.append(imgNode);
7760
7618
  });
7761
7619
  }
7762
7620
  const imageBox = {
@@ -7797,46 +7655,193 @@ const imageBox = {
7797
7655
  if (value.status === 'loading') {
7798
7656
  return;
7799
7657
  }
7800
- const imageNode = query('<div class="lake-image" />');
7801
- imageNode.addClass(`lake-image-${value.status}`);
7658
+ const rootNode = query('<div class="lake-image" />');
7659
+ rootNode.addClass(`lake-image-${value.status}`);
7802
7660
  let promise;
7803
7661
  if (value.status === 'uploading') {
7804
- promise = renderUploading(imageNode, box);
7662
+ promise = renderUploading(rootNode, box);
7805
7663
  }
7806
7664
  else if (value.status === 'error') {
7807
- promise = renderError(imageNode, box);
7665
+ promise = renderError(rootNode, box);
7808
7666
  }
7809
7667
  else {
7810
- promise = renderDone(imageNode, box);
7668
+ promise = renderDone(rootNode, box);
7811
7669
  }
7812
7670
  promise.then(() => {
7813
7671
  container.empty();
7814
- container.append(imageNode);
7815
- imageNode.find('.lake-button-view').on('click', () => openFullScreen(box));
7672
+ container.append(rootNode);
7673
+ rootNode.find('.lake-button-view').on('click', () => openFullScreen(box));
7816
7674
  if (editor.readonly) {
7817
- imageNode.find('.lake-button-remove').hide();
7675
+ rootNode.find('.lake-button-remove').hide();
7818
7676
  }
7819
7677
  else {
7820
- imageNode.find('.lake-button-remove').on('click', event => {
7678
+ rootNode.find('.lake-button-remove').on('click', event => {
7821
7679
  event.stopPropagation();
7822
- const xhr = box.getData('xhr');
7823
- if (xhr) {
7824
- xhr.abort();
7825
- }
7826
7680
  editor.selection.removeBox(box);
7827
7681
  editor.history.save();
7828
- editor.selection.sync();
7829
7682
  });
7830
7683
  }
7831
7684
  box.event.emit('render');
7832
7685
  });
7833
- imageNode.on('click', () => {
7686
+ rootNode.on('click', () => {
7834
7687
  editor.selection.selectBox(box);
7835
7688
  });
7836
7689
  },
7837
7690
  html: box => {
7838
- const value = box.node.attr('value');
7839
- return safeTemplate `<img src="${box.value.url}" data-lake-value="${value}" />`;
7691
+ const rawValue = box.node.attr('value');
7692
+ return safeTemplate `<img src="${box.value.url}" data-lake-value="${rawValue}" />`;
7693
+ },
7694
+ };
7695
+
7696
+ function getVideoId(url) {
7697
+ const result = /\w+$/i.exec(url || '');
7698
+ return result ? result[0] : '';
7699
+ }
7700
+ function getInputValue(videoNode, name) {
7701
+ const inputElement = videoNode.find(`input[name="${name}"]`);
7702
+ const nativeInputElement = inputElement.get(0);
7703
+ return nativeInputElement.value;
7704
+ }
7705
+ function appendButtonGroup(box) {
7706
+ const editor = box.getEditor();
7707
+ if (!editor) {
7708
+ return;
7709
+ }
7710
+ const boxContainer = box.getContainer();
7711
+ const videoNode = boxContainer.find('.lake-video');
7712
+ const buttonGroupNode = query(safeTemplate `
7713
+ <div class="lake-button-group">
7714
+ <button type="button" tabindex="-1" class="lake-button-remove" title="${editor.locale.video.remove()}"></button>
7715
+ </div>
7716
+ `);
7717
+ const removeButton = buttonGroupNode.find('.lake-button-remove');
7718
+ const removeIcon = icons.get('remove');
7719
+ if (removeIcon) {
7720
+ removeButton.append(removeIcon);
7721
+ }
7722
+ buttonGroupNode.find('.lake-button-remove').on('click', event => {
7723
+ event.stopPropagation();
7724
+ editor.selection.removeBox(box);
7725
+ editor.history.save();
7726
+ });
7727
+ videoNode.append(buttonGroupNode);
7728
+ }
7729
+ function showVideo(box) {
7730
+ const editor = box.getEditor();
7731
+ if (!editor) {
7732
+ return;
7733
+ }
7734
+ const boxContainer = box.getContainer();
7735
+ const value = box.value;
7736
+ const width = value.width || 560;
7737
+ const height = value.height || 315;
7738
+ boxContainer.css({
7739
+ width: `${width}px`,
7740
+ height: `${height}px`,
7741
+ });
7742
+ const videoId = getVideoId(value.url);
7743
+ if (videoId === '') {
7744
+ throw new Error(`Invalid link: ${value.url}`);
7745
+ }
7746
+ // YouTube URL: https://www.youtube.com/watch?v=5sMBhDv4sik
7747
+ // The script for embedding YouTube:
7748
+ // <iframe width="560" height="315" src="https://www.youtube.com/embed/5sMBhDv4sik" title="YouTube video player"
7749
+ // frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7750
+ // referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7751
+ const iframeNode = query(safeTemplate `
7752
+ <iframe width="100%" height="${height}" src="https://www.youtube.com/embed/${videoId}" title="YouTube video player"
7753
+ frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
7754
+ referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
7755
+ `);
7756
+ const rootNode = boxContainer.find('.lake-video');
7757
+ if (!editor.readonly) {
7758
+ iframeNode.on('load', () => {
7759
+ appendButtonGroup(box);
7760
+ new BoxResizer({
7761
+ root: rootNode,
7762
+ box,
7763
+ width,
7764
+ height,
7765
+ onResize: (newWidth, newHeight) => {
7766
+ iframeNode.attr({
7767
+ height: newHeight.toString(),
7768
+ });
7769
+ },
7770
+ onStop: (newWidth, newHeight) => {
7771
+ box.updateValue({
7772
+ width: newWidth,
7773
+ height: newHeight,
7774
+ });
7775
+ editor.history.save();
7776
+ },
7777
+ }).render();
7778
+ });
7779
+ }
7780
+ rootNode.append(iframeNode);
7781
+ }
7782
+ const videoBox = {
7783
+ type: 'inline',
7784
+ name: 'video',
7785
+ render: box => {
7786
+ const editor = box.getEditor();
7787
+ if (!editor) {
7788
+ return;
7789
+ }
7790
+ const locale = editor.locale;
7791
+ const value = box.value;
7792
+ const boxContainer = box.getContainer();
7793
+ const rootNode = query('<div class="lake-video" />');
7794
+ boxContainer.empty();
7795
+ boxContainer.css({
7796
+ width: '',
7797
+ height: '',
7798
+ });
7799
+ boxContainer.append(rootNode);
7800
+ if (!value.url) {
7801
+ if (editor.readonly) {
7802
+ box.node.hide();
7803
+ return;
7804
+ }
7805
+ const formNode = query(safeTemplate `
7806
+ <div class="lake-video-form">
7807
+ <div class="lake-row lake-desc-row">${locale.video.description()}</div>
7808
+ <div class="lake-row">${locale.video.url()}</div>
7809
+ <div class="lake-row">
7810
+ <input type="text" name="url" placeholder="https://www.youtube.com/watch?v=..." />
7811
+ </div>
7812
+ <div class="lake-row lake-button-row"></div>
7813
+ </div>
7814
+ `);
7815
+ const button = new Button({
7816
+ root: formNode.find('.lake-button-row'),
7817
+ name: 'embed',
7818
+ type: 'primary',
7819
+ text: locale.video.embed(),
7820
+ onClick: () => {
7821
+ const url = getInputValue(formNode, 'url');
7822
+ if (url.indexOf('https://www.youtube.com/') < 0 || getVideoId(url) === '') {
7823
+ editor.config.onMessage('error', locale.video.urlError());
7824
+ return;
7825
+ }
7826
+ box.updateValue('url', url);
7827
+ editor.history.save();
7828
+ formNode.remove();
7829
+ showVideo(box);
7830
+ },
7831
+ });
7832
+ formNode.find('input[name="url"]').on('keydown', createKeybindingsHandler({
7833
+ 'Enter': event => {
7834
+ event.preventDefault();
7835
+ button.node.emit('click');
7836
+ },
7837
+ }));
7838
+ button.render();
7839
+ rootNode.append(formNode);
7840
+ appendButtonGroup(box);
7841
+ }
7842
+ else {
7843
+ showVideo(box);
7844
+ }
7840
7845
  },
7841
7846
  };
7842
7847
 
@@ -7862,11 +7867,10 @@ const boxToolbarItems = [
7862
7867
  }
7863
7868
  editor.selection.removeBox(box);
7864
7869
  editor.history.save();
7865
- editor.selection.sync();
7866
7870
  },
7867
7871
  },
7868
7872
  ];
7869
- function appendContent(fileNode, box) {
7873
+ function appendContent(rootNode, box) {
7870
7874
  return __awaiter(this, void 0, void 0, function* () {
7871
7875
  const editor = box.getEditor();
7872
7876
  if (!editor) {
@@ -7899,7 +7903,7 @@ function appendContent(fileNode, box) {
7899
7903
  typeNode.append(fileIcon);
7900
7904
  }
7901
7905
  }
7902
- fileNode.append(infoNode);
7906
+ rootNode.append(infoNode);
7903
7907
  });
7904
7908
  }
7905
7909
  const fileBox = {
@@ -7916,26 +7920,45 @@ const fileBox = {
7916
7920
  return;
7917
7921
  }
7918
7922
  const container = box.getContainer();
7919
- const fileNode = query('<div class="lake-file" />');
7920
- fileNode.addClass(`lake-file-${value.status}`);
7921
- appendContent(fileNode, box);
7923
+ const rootNode = query('<div class="lake-file" />');
7924
+ rootNode.addClass(`lake-file-${value.status}`);
7925
+ appendContent(rootNode, box);
7922
7926
  container.empty();
7923
- container.append(fileNode);
7927
+ container.append(rootNode);
7924
7928
  if (!editor.readonly) {
7925
- fileNode.on('click', () => {
7929
+ rootNode.on('click', () => {
7926
7930
  editor.selection.selectBox(box);
7927
7931
  });
7928
7932
  const items = value.status === 'done' ? boxToolbarItems : boxToolbarItems.filter(item => item.name === 'remove');
7929
7933
  box.setToolbar(items);
7930
7934
  }
7931
7935
  else {
7932
- fileNode.on('click', () => {
7936
+ rootNode.on('click', () => {
7933
7937
  window.open(value.url);
7934
7938
  });
7935
7939
  }
7936
7940
  },
7937
7941
  };
7938
7942
 
7943
+ const emojiBox = {
7944
+ type: 'inline',
7945
+ name: 'emoji',
7946
+ render: box => {
7947
+ const editor = box.getEditor();
7948
+ if (!editor) {
7949
+ return;
7950
+ }
7951
+ const value = box.value;
7952
+ const rootNode = query(safeTemplate `
7953
+ <div class="lake-emoji"><img src="${value.url}" title="${value.title}" /></div>
7954
+ `);
7955
+ box.getContainer().append(rootNode);
7956
+ rootNode.on('click', () => {
7957
+ editor.selection.selectBox(box);
7958
+ });
7959
+ },
7960
+ };
7961
+
7939
7962
  var copy = (editor) => {
7940
7963
  editor.event.on('copy', event => {
7941
7964
  const range = editor.selection.range;
@@ -8554,7 +8577,6 @@ var list = (editor) => {
8554
8577
  }
8555
8578
  }
8556
8579
  editor.history.save();
8557
- editor.selection.sync();
8558
8580
  },
8559
8581
  });
8560
8582
  editor.container.on('click', event => {
@@ -9166,14 +9188,12 @@ var link = (editor) => {
9166
9188
  const range = editor.selection.range;
9167
9189
  range.setStartAfter(node);
9168
9190
  range.collapseToStart();
9169
- editor.selection.sync();
9170
9191
  editor.history.save();
9171
9192
  },
9172
9193
  onRemove: node => {
9173
9194
  const range = editor.selection.range;
9174
9195
  range.setStartAfter(node);
9175
9196
  range.collapseToStart();
9176
- editor.selection.sync();
9177
9197
  editor.history.save();
9178
9198
  },
9179
9199
  });
@@ -9232,21 +9252,6 @@ var hr = (editor) => {
9232
9252
  });
9233
9253
  };
9234
9254
 
9235
- var video = (editor) => {
9236
- if (editor.readonly) {
9237
- return;
9238
- }
9239
- editor.command.add('video', {
9240
- execute: (value) => {
9241
- const box = editor.selection.insertBox('video', value);
9242
- editor.history.save();
9243
- if (box) {
9244
- box.getContainer().find('input[name="url"]').focus();
9245
- }
9246
- },
9247
- });
9248
- };
9249
-
9250
9255
  const langList = [
9251
9256
  'text',
9252
9257
  'c',
@@ -9282,8 +9287,7 @@ var codeBlock = (editor) => {
9282
9287
  execute: (value) => {
9283
9288
  const box = editor.selection.insertBox('codeBlock', value);
9284
9289
  editor.history.save();
9285
- const codeEditor = box.getData('codeEditor');
9286
- codeEditor.focus();
9290
+ box.getContainer().find('.lake-code-block').emit('click');
9287
9291
  },
9288
9292
  });
9289
9293
  };
@@ -9322,6 +9326,21 @@ var image = (editor) => {
9322
9326
  });
9323
9327
  };
9324
9328
 
9329
+ var video = (editor) => {
9330
+ if (editor.readonly) {
9331
+ return;
9332
+ }
9333
+ editor.command.add('video', {
9334
+ execute: (value) => {
9335
+ const box = editor.selection.insertBox('video', value);
9336
+ editor.history.save();
9337
+ if (box) {
9338
+ box.getContainer().find('input[name="url"]').focus();
9339
+ }
9340
+ },
9341
+ });
9342
+ };
9343
+
9325
9344
  var file = (editor) => {
9326
9345
  editor.setPluginConfig('file', {
9327
9346
  requestMethod: 'POST',
@@ -9355,6 +9374,18 @@ var file = (editor) => {
9355
9374
  });
9356
9375
  };
9357
9376
 
9377
+ var emoji = (editor) => {
9378
+ if (editor.readonly) {
9379
+ return;
9380
+ }
9381
+ editor.command.add('emoji', {
9382
+ execute: (value) => {
9383
+ editor.selection.insertBox('emoji', value);
9384
+ editor.history.save();
9385
+ },
9386
+ });
9387
+ };
9388
+
9358
9389
  const headingTypeMap = new Map([
9359
9390
  ['#', 'h1'],
9360
9391
  ['##', 'h2'],
@@ -9554,7 +9585,7 @@ function executeMarkCommand(editor, point) {
9554
9585
  // <p>foobold\u200B<focus /></p>,
9555
9586
  // to
9556
9587
  // <p>foo[bold]\u200B<focus /></p>, startOffset = 3, endOffset = 7
9557
- editor.prepareOperation();
9588
+ editor.history.pause();
9558
9589
  const bookmark = selection.insertBookmark();
9559
9590
  const node = bookmark.focus.prev();
9560
9591
  const oldValue = node.text();
@@ -9564,7 +9595,8 @@ function executeMarkCommand(editor, point) {
9564
9595
  range.setEnd(node, offset - (oldValue.length - newValue.length) - 1);
9565
9596
  editor.command.execute(commandName, ...parameters);
9566
9597
  selection.toBookmark(bookmark);
9567
- editor.commitOperation();
9598
+ editor.history.continue();
9599
+ editor.history.save();
9568
9600
  return true;
9569
9601
  }
9570
9602
  }
@@ -9586,16 +9618,14 @@ function spaceKeyExecutesBlockCommand(editor, point) {
9586
9618
  // <p>#<focus />foo</p>
9587
9619
  // to
9588
9620
  // <h1><focus />foo</h1>
9589
- editor.prepareOperation();
9590
9621
  const bookmark = selection.insertBookmark();
9591
9622
  const node = bookmark.focus.prev();
9592
9623
  node.remove();
9593
9624
  const block = bookmark.focus.closestBlock();
9594
9625
  fixEmptyBlock(block);
9595
9626
  selection.range.shrinkAfter(block);
9596
- editor.command.execute(commandName, ...parameters);
9597
9627
  selection.toBookmark(bookmark);
9598
- editor.commitOperation();
9628
+ editor.command.execute(commandName, ...parameters);
9599
9629
  return true;
9600
9630
  }
9601
9631
  }
@@ -9616,12 +9646,10 @@ function enterKeyExecutesBlockCommand(editor, block) {
9616
9646
  // <p>---<focus /></p>
9617
9647
  // to
9618
9648
  // <lake-box type="block" name="hr" focus="end"></lake-box>
9619
- editor.prepareOperation();
9620
9649
  block.empty();
9621
9650
  fixEmptyBlock(block);
9622
9651
  selection.range.shrinkAfter(block);
9623
9652
  editor.command.execute(commandName, ...parameters);
9624
- editor.commitOperation();
9625
9653
  return true;
9626
9654
  }
9627
9655
  }
@@ -9700,7 +9728,7 @@ function splitBlock(editor, block) {
9700
9728
  block.find('li').attr('value', 'false');
9701
9729
  }
9702
9730
  }
9703
- function addBlockOrSplitBlockForBox(editor) {
9731
+ function addOrSplitBlockForBox(editor) {
9704
9732
  const range = editor.selection.range;
9705
9733
  const boxNode = range.startNode.closest('lake-box');
9706
9734
  const block = boxNode.closestBlock();
@@ -9744,7 +9772,7 @@ var enterKey = (editor) => {
9744
9772
  event.preventDefault();
9745
9773
  editor.fixContent();
9746
9774
  if (range.isBox) {
9747
- addBlockOrSplitBlockForBox(editor);
9775
+ addOrSplitBlockForBox(editor);
9748
9776
  editor.history.save();
9749
9777
  return;
9750
9778
  }
@@ -9753,7 +9781,7 @@ var enterKey = (editor) => {
9753
9781
  return;
9754
9782
  }
9755
9783
  if (range.isBox) {
9756
- addBlockOrSplitBlockForBox(editor);
9784
+ addOrSplitBlockForBox(editor);
9757
9785
  editor.history.save();
9758
9786
  return;
9759
9787
  }
@@ -9779,7 +9807,9 @@ function addLineBreak(editor) {
9779
9807
  const prevNode = range.getPrevNode();
9780
9808
  const endText = range.getEndText();
9781
9809
  if (prevNode.name !== 'br' && endText === '') {
9782
- editor.selection.insertContents('<br /><br />');
9810
+ const fragment = new Fragment();
9811
+ fragment.append('<br /><br />');
9812
+ editor.selection.insertFragment(fragment);
9783
9813
  editor.history.save();
9784
9814
  return;
9785
9815
  }
@@ -9848,6 +9878,14 @@ var shiftEnterKey = (editor) => {
9848
9878
  });
9849
9879
  };
9850
9880
 
9881
+ function removeEmptyMarks(range) {
9882
+ const block = range.getBlocks()[0];
9883
+ if (block && block.isEmpty && block.first().name !== 'br') {
9884
+ block.empty();
9885
+ appendDeepest(block, query('<br />'));
9886
+ range.shrinkAfter(block);
9887
+ }
9888
+ }
9851
9889
  function mergeWithPreviousBlock(editor, block) {
9852
9890
  const range = editor.selection.range;
9853
9891
  let prevBlock = block.prev();
@@ -9878,6 +9916,7 @@ function mergeWithPreviousBlock(editor, block) {
9878
9916
  prevBlock.remove();
9879
9917
  return;
9880
9918
  }
9919
+ removeEmptyMarks(range);
9881
9920
  const bookmark = editor.selection.insertBookmark();
9882
9921
  mergeNodes(prevBlock, block);
9883
9922
  editor.selection.toBookmark(bookmark);
@@ -9890,6 +9929,14 @@ var backspaceKey = (editor) => {
9890
9929
  editor.keystroke.setKeydown('backspace', event => {
9891
9930
  const range = editor.selection.range;
9892
9931
  if (range.isInsideBox) {
9932
+ const boxNode = range.commonAncestor.closest('lake-box');
9933
+ const box = getBox(boxNode);
9934
+ const boxValue = box.value;
9935
+ if (box.name === 'codeBlock' && (boxValue.code === undefined || boxValue.code === '')) {
9936
+ event.preventDefault();
9937
+ editor.selection.removeBox(box);
9938
+ editor.history.save();
9939
+ }
9893
9940
  return;
9894
9941
  }
9895
9942
  editor.fixContent();
@@ -9948,7 +9995,7 @@ var backspaceKey = (editor) => {
9948
9995
  editor.history.save();
9949
9996
  return;
9950
9997
  }
9951
- if (prevNode.isText && prevNode.text().length === 1) {
9998
+ if (prevNode.isText && prevNode.text().length === 1 && prevNode.parent().isBlock) {
9952
9999
  event.preventDefault();
9953
10000
  const block = prevNode.closestBlock();
9954
10001
  range.setStartBefore(prevNode);
@@ -10111,13 +10158,14 @@ var tabKey = (editor) => {
10111
10158
  }
10112
10159
  event.preventDefault();
10113
10160
  const blocks = editor.selection.range.getBlocks();
10114
- blocks.forEach(block => {
10161
+ for (const block of blocks) {
10115
10162
  if (block.name !== 'p' || block.css('text-indent') === '2em') {
10116
10163
  setBlockIndent(block, 'increase');
10117
- return;
10118
10164
  }
10119
- block.css('text-indent', '2em');
10120
- });
10165
+ else {
10166
+ block.css('text-indent', '2em');
10167
+ }
10168
+ }
10121
10169
  editor.history.save();
10122
10170
  });
10123
10171
  };
@@ -10268,10 +10316,11 @@ var escapeKey = (editor) => {
10268
10316
  };
10269
10317
 
10270
10318
  Editor.box.add(hrBox);
10271
- Editor.box.add(videoBox);
10272
10319
  Editor.box.add(codeBlockBox);
10273
10320
  Editor.box.add(imageBox);
10321
+ Editor.box.add(videoBox);
10274
10322
  Editor.box.add(fileBox);
10323
+ Editor.box.add(emojiBox);
10275
10324
  Editor.plugin.add(copy);
10276
10325
  Editor.plugin.add(cut);
10277
10326
  Editor.plugin.add(paste);
@@ -10299,10 +10348,11 @@ Editor.plugin.add(removeFormat);
10299
10348
  Editor.plugin.add(formatPainter);
10300
10349
  Editor.plugin.add(link);
10301
10350
  Editor.plugin.add(hr);
10302
- Editor.plugin.add(video);
10303
10351
  Editor.plugin.add(codeBlock);
10304
10352
  Editor.plugin.add(image);
10353
+ Editor.plugin.add(video);
10305
10354
  Editor.plugin.add(file);
10355
+ Editor.plugin.add(emoji);
10306
10356
  Editor.plugin.add(markdown);
10307
10357
  Editor.plugin.add(enterKey);
10308
10358
  Editor.plugin.add(shiftEnterKey);
@@ -10312,5 +10362,5 @@ Editor.plugin.add(tabKey);
10312
10362
  Editor.plugin.add(arrowKeys);
10313
10363
  Editor.plugin.add(escapeKey);
10314
10364
 
10315
- 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 };
10365
+ 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 };
10316
10366
  //# sourceMappingURL=lake.js.map