lakelib 0.1.13 → 0.1.14

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
@@ -2029,10 +2029,15 @@ function wrapNodeList(nodeList, wrapper) {
2029
2029
  // Removes Zero-width spaces that are dependent on some other text nodes.
2030
2030
  function removeZWS(node) {
2031
2031
  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, '');
2032
+ if (child.isText) {
2033
+ const text = child.text();
2034
+ if (text === '') {
2035
+ child.remove();
2036
+ }
2037
+ else if (text.length > 1) {
2038
+ if (/\u200B/.test(text)) {
2039
+ child.get(0).nodeValue = text.replace(/\u200B/g, '');
2040
+ }
2036
2041
  }
2037
2042
  }
2038
2043
  }
@@ -3566,7 +3571,7 @@ class Box {
3566
3571
  container.removeClass('lake-box-hovered');
3567
3572
  });
3568
3573
  container.on('click', () => {
3569
- debug(`Box '${this.name}' (id = ${this.node.id}) value:`);
3574
+ debug(`Box "${this.name}" (id = ${this.node.id}) value:`);
3570
3575
  debug(this.value);
3571
3576
  });
3572
3577
  if (this.type === 'block' && this.node.isContentEditable) {
@@ -3667,7 +3672,7 @@ class Box {
3667
3672
  newContainer.append(content);
3668
3673
  morph(container, newContainer);
3669
3674
  }
3670
- debug(`Box '${this.name}' (id: ${this.node.id}) rendered`);
3675
+ debug(`Box "${this.name}" (id: ${this.node.id}) rendered`);
3671
3676
  }
3672
3677
  // Destroys a rendered box.
3673
3678
  unmount() {
@@ -3676,7 +3681,7 @@ class Box {
3676
3681
  boxData[this.node.id] = {};
3677
3682
  this.event.removeAllListeners();
3678
3683
  this.node.empty();
3679
- debug(`Box '${this.name}' (id: ${this.node.id}) unmounted`);
3684
+ debug(`Box "${this.name}" (id: ${this.node.id}) unmounted`);
3680
3685
  }
3681
3686
  // Returns a HTML string of the box.
3682
3687
  getHTML() {
@@ -4287,9 +4292,8 @@ function insertBookmark(range) {
4287
4292
  function removeAndNormalizeNode(node, range) {
4288
4293
  const previousNode = node.prev();
4289
4294
  const nextNode = node.next();
4290
- if (previousNode.isText && nextNode.isText) {
4295
+ if (previousNode.isText || nextNode.isText) {
4291
4296
  const parentNode = node.parent();
4292
- removeZWS(parentNode);
4293
4297
  node.remove();
4294
4298
  parentNode.get(0).normalize();
4295
4299
  }
@@ -4593,7 +4597,7 @@ function splitBlock$1(range) {
4593
4597
  }
4594
4598
 
4595
4599
  // Removes empty marks that contain no content.
4596
- function removeEmptyMarks$1(node) {
4600
+ function removeEmptyMarks$2(node) {
4597
4601
  if (node.isMark && node.isEmpty) {
4598
4602
  node.remove();
4599
4603
  return;
@@ -4615,8 +4619,8 @@ function splitMarksAtPoint(node, offset, removeEmptyMark) {
4615
4619
  const parts = splitNodes(node, offset, limitBlock);
4616
4620
  if (parts) {
4617
4621
  if (removeEmptyMark) {
4618
- removeEmptyMarks$1(parts.start);
4619
- removeEmptyMarks$1(parts.end);
4622
+ removeEmptyMarks$2(parts.start);
4623
+ removeEmptyMarks$2(parts.end);
4620
4624
  if (!parts.start.isEmpty) {
4621
4625
  start = parts.start;
4622
4626
  }
@@ -4802,7 +4806,7 @@ function addMark(range, value) {
4802
4806
  }
4803
4807
 
4804
4808
  // Removes empty marks that contain no content.
4805
- function removeEmptyMarks(node) {
4809
+ function removeEmptyMarks$1(node) {
4806
4810
  if (node.isMark && node.isEmpty) {
4807
4811
  node.remove();
4808
4812
  return;
@@ -4860,13 +4864,13 @@ function removeMark(range, value) {
4860
4864
  return;
4861
4865
  }
4862
4866
  if (parts.end) {
4863
- removeEmptyMarks(parts.end);
4867
+ removeEmptyMarks$1(parts.end);
4864
4868
  }
4865
4869
  const zeroWidthSpace = new Nodes(document.createTextNode('\u200B'));
4866
4870
  const newMark = copyNestedMarks(parts.start, tagName);
4867
4871
  if (!newMark) {
4868
4872
  parts.start.after(zeroWidthSpace);
4869
- removeEmptyMarks(parts.start);
4873
+ removeEmptyMarks$1(parts.start);
4870
4874
  if (zeroWidthSpace.prev().isText) {
4871
4875
  range.setStartAfter(zeroWidthSpace.prev());
4872
4876
  range.collapseToStart();
@@ -4879,7 +4883,7 @@ function removeMark(range, value) {
4879
4883
  }
4880
4884
  appendDeepest(newMark, zeroWidthSpace);
4881
4885
  parts.start.after(newMark);
4882
- removeEmptyMarks(parts.start);
4886
+ removeEmptyMarks$1(parts.start);
4883
4887
  range.shrinkAfter(newMark);
4884
4888
  return;
4885
4889
  }
@@ -4947,7 +4951,7 @@ function insertLink(range, value) {
4947
4951
  return linkNode;
4948
4952
  }
4949
4953
 
4950
- var version = "0.1.13";
4954
+ var version = "0.1.14";
4951
4955
 
4952
4956
  // Inserts a box into the specified range.
4953
4957
  function insertBox(range, boxName, boxValue) {
@@ -5116,7 +5120,10 @@ class Selection {
5116
5120
  // Updates the saved range with the range of the native selection.
5117
5121
  updateByRange() {
5118
5122
  const newRange = this.getRangeFromNativeSelection();
5119
- if (this.range.get() === newRange.get()) {
5123
+ if (this.range.startNode.get(0) === newRange.startNode.get(0) &&
5124
+ this.range.startOffset === newRange.startOffset &&
5125
+ this.range.endNode.get(0) === newRange.endNode.get(0) &&
5126
+ this.range.endOffset === newRange.endOffset) {
5120
5127
  return;
5121
5128
  }
5122
5129
  this.range = newRange;
@@ -5218,7 +5225,6 @@ class Selection {
5218
5225
  class Command {
5219
5226
  constructor(selection) {
5220
5227
  this.commandMap = new Map();
5221
- this.event = new EventEmitter();
5222
5228
  this.selection = selection;
5223
5229
  }
5224
5230
  add(name, commandItem) {
@@ -5266,10 +5272,8 @@ class Command {
5266
5272
  }
5267
5273
  execute(name, ...data) {
5268
5274
  const commandItem = this.getItem(name);
5269
- this.event.emit('beforeexecute', name);
5270
5275
  commandItem.execute.apply(this, data);
5271
- this.event.emit('execute', name);
5272
- debug(`Command '${name}' executed`);
5276
+ debug(`Command "${name}" executed`);
5273
5277
  }
5274
5278
  }
5275
5279
 
@@ -5287,11 +5291,11 @@ class Command {
5287
5291
  // inputs 'e': value: 'abe', list: ['a', 'ab', 'abe'], index: 3, canRedo: false
5288
5292
  class History {
5289
5293
  constructor(selection) {
5294
+ this.canSave = true;
5290
5295
  // an array for storing the history items
5291
5296
  this.list = [];
5292
5297
  // the next index of the list
5293
5298
  this.index = 0;
5294
- this.canSave = true;
5295
5299
  this.limit = 100;
5296
5300
  this.event = new EventEmitter();
5297
5301
  this.selection = selection;
@@ -5343,6 +5347,12 @@ class History {
5343
5347
  this.removeIdfromBoxes(container);
5344
5348
  this.removeIdfromBoxes(otherContainer);
5345
5349
  }
5350
+ get canUndo() {
5351
+ return this.index > 1 && !!this.list[this.index - 1];
5352
+ }
5353
+ get canRedo() {
5354
+ return !!this.list[this.index];
5355
+ }
5346
5356
  cloneContainer() {
5347
5357
  const range = this.selection.range;
5348
5358
  const newContainer = this.container.clone(true);
@@ -5354,6 +5364,12 @@ class History {
5354
5364
  return newContainer;
5355
5365
  }
5356
5366
  if (range.isInsideBox) {
5367
+ const boxNode = range.commonAncestor.closest('lake-box');
5368
+ const boxNodePath = boxNode.path();
5369
+ const newBoxNode = newContainer.find(boxNodePath);
5370
+ const newRange = range.clone();
5371
+ newRange.selectBox(newBoxNode);
5372
+ insertBookmark(newRange);
5357
5373
  return newContainer;
5358
5374
  }
5359
5375
  const startNodePath = range.startNode.path();
@@ -5366,23 +5382,14 @@ class History {
5366
5382
  insertBookmark(newRange);
5367
5383
  return newContainer;
5368
5384
  }
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
5385
  undo() {
5379
- if (!this.list[this.index - 1]) {
5386
+ if (!this.list[this.index - 2]) {
5380
5387
  return;
5381
5388
  }
5382
5389
  this.selection.insertBookmark();
5383
5390
  const value = this.getValue(this.container);
5384
- while (this.index > 0) {
5385
- const prevItem = this.list[this.index - 1];
5391
+ while (this.index > 1) {
5392
+ const prevItem = this.list[this.index - 2];
5386
5393
  if (!prevItem) {
5387
5394
  break;
5388
5395
  }
@@ -5394,9 +5401,6 @@ class History {
5394
5401
  break;
5395
5402
  }
5396
5403
  }
5397
- if (this.index < 1) {
5398
- this.index = 1;
5399
- }
5400
5404
  this.selection.updateByBookmark();
5401
5405
  debug(`History undone (index: ${this.index})`);
5402
5406
  }
@@ -5428,7 +5432,11 @@ class History {
5428
5432
  pause() {
5429
5433
  this.canSave = false;
5430
5434
  }
5431
- save(emitSaveEvent = true) {
5435
+ save(options = {}) {
5436
+ var _a, _b, _c;
5437
+ const inputType = (_a = options.inputType) !== null && _a !== void 0 ? _a : '';
5438
+ const update = (_b = options.update) !== null && _b !== void 0 ? _b : false;
5439
+ const emitEvent = (_c = options.emitEvent) !== null && _c !== void 0 ? _c : true;
5432
5440
  if (!this.canSave) {
5433
5441
  return;
5434
5442
  }
@@ -5438,16 +5446,25 @@ class History {
5438
5446
  this.removeBookmark(this.getValue(this.list[this.index - 1])) === this.removeBookmark(value)) {
5439
5447
  return;
5440
5448
  }
5441
- this.list.splice(this.index, Infinity, item);
5442
- this.index++;
5449
+ if (update) {
5450
+ this.list.splice(this.index - 1, Infinity, item);
5451
+ }
5452
+ else {
5453
+ this.list.splice(this.index, Infinity, item);
5454
+ this.index++;
5455
+ }
5443
5456
  if (this.list.length > this.limit) {
5444
5457
  this.list.shift();
5445
5458
  this.index = this.list.length;
5446
5459
  }
5447
- if (emitSaveEvent) {
5448
- this.event.emit('save', denormalizeValue(value));
5460
+ debug(`History saved (index: ${this.index}, inputType: "${inputType}", update: ${update}, emitEvent: ${emitEvent})`);
5461
+ if (emitEvent) {
5462
+ this.event.emit('save', denormalizeValue(value), {
5463
+ inputType,
5464
+ update,
5465
+ emitEvent,
5466
+ });
5449
5467
  }
5450
- debug(`History saved (index: ${this.index})`);
5451
5468
  }
5452
5469
  }
5453
5470
 
@@ -5571,6 +5588,7 @@ const defaultConfig = {
5571
5588
  class Editor {
5572
5589
  constructor(config) {
5573
5590
  this.unsavedInputData = '';
5591
+ this.unsavedInputCount = 0;
5574
5592
  this.state = {
5575
5593
  appliedItems: [],
5576
5594
  disabledNameMap: new Map(),
@@ -5601,9 +5619,6 @@ class Editor {
5601
5619
  }
5602
5620
  this.event.emit('paste', event);
5603
5621
  };
5604
- this.beforeunloadListener = () => {
5605
- this.history.save();
5606
- };
5607
5622
  this.selectionchangeListener = () => {
5608
5623
  this.selection.updateByRange();
5609
5624
  this.updateBoxSelectionStyle();
@@ -5619,6 +5634,7 @@ class Editor {
5619
5634
  this.resizeListener = () => {
5620
5635
  this.event.emit('resize');
5621
5636
  };
5637
+ // Updates the classes of all boxes when the current selection of the editor is changed.
5622
5638
  this.updateBoxSelectionStyle = debounce(() => {
5623
5639
  // The editor has been unmounted.
5624
5640
  if (this.root.first().length === 0) {
@@ -5669,6 +5685,7 @@ class Editor {
5669
5685
  trailing: true,
5670
5686
  maxWait: 50,
5671
5687
  });
5688
+ // Triggers the statechange event when the current selection of the editor is changed.
5672
5689
  this.emitStateChangeEvent = debounce(() => {
5673
5690
  const commandNames = this.command.getNames();
5674
5691
  let appliedItems = this.selection.getAppliedItems();
@@ -5715,13 +5732,6 @@ class Editor {
5715
5732
  trailing: true,
5716
5733
  maxWait: 100,
5717
5734
  });
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
5735
  if (!config.root) {
5726
5736
  throw new Error('The root of the config must be specified.');
5727
5737
  }
@@ -5751,6 +5761,7 @@ class Editor {
5751
5761
  this.keystroke = new Keystroke(this.container);
5752
5762
  editors.set(this.container.id, this);
5753
5763
  }
5764
+ // Adds or Removes a placeholder class.
5754
5765
  togglePlaceholderClass(value) {
5755
5766
  value = denormalizeValue(value);
5756
5767
  const className = 'lake-show-placeholder';
@@ -5761,7 +5772,8 @@ class Editor {
5761
5772
  this.container.removeClass(className);
5762
5773
  }
5763
5774
  }
5764
- inputInBoxStrip() {
5775
+ // Moves the input text from box strip to normal position.
5776
+ moveBoxStripText() {
5765
5777
  const selection = this.selection;
5766
5778
  const range = selection.range;
5767
5779
  const stripNode = range.startNode.closest('.lake-box-strip');
@@ -5791,6 +5803,12 @@ class Editor {
5791
5803
  stripNode.html('<br />');
5792
5804
  selection.insertNode(document.createTextNode(text));
5793
5805
  }
5806
+ // Resets the value of unsaved input property.
5807
+ resetUnsavedInputData() {
5808
+ this.unsavedInputData = '';
5809
+ this.unsavedInputCount = 0;
5810
+ }
5811
+ // Binds events about input.
5794
5812
  bindInputEvents() {
5795
5813
  this.container.on('compositionstart', () => {
5796
5814
  this.isComposing = true;
@@ -5798,19 +5816,6 @@ class Editor {
5798
5816
  this.container.on('compositionend', () => {
5799
5817
  this.isComposing = false;
5800
5818
  });
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
5819
  this.container.on('input', event => {
5815
5820
  const inputEvent = event;
5816
5821
  // Here setTimeout is necessary because isComposing is not false after ending composition.
@@ -5826,7 +5831,7 @@ class Editor {
5826
5831
  return;
5827
5832
  }
5828
5833
  if (range.isBoxStart || range.isBoxEnd) {
5829
- this.inputInBoxStrip();
5834
+ this.moveBoxStripText();
5830
5835
  this.history.save();
5831
5836
  this.event.emit('input', inputEvent);
5832
5837
  return;
@@ -5834,30 +5839,60 @@ class Editor {
5834
5839
  if (inputEvent.inputType === 'insertText' ||
5835
5840
  inputEvent.inputType === 'insertCompositionText') {
5836
5841
  this.unsavedInputData += (_a = inputEvent.data) !== null && _a !== void 0 ? _a : '';
5842
+ this.unsavedInputCount++;
5837
5843
  if (this.unsavedInputData.length < this.config.minChangeSize) {
5838
- this.event.emit('input', inputEvent);
5839
- this.emitChangeEvent(this.getValue());
5840
- return;
5844
+ this.history.save({
5845
+ inputType: 'insertText',
5846
+ update: this.unsavedInputCount > 1,
5847
+ });
5841
5848
  }
5849
+ else {
5850
+ this.history.save({
5851
+ inputType: 'insertText',
5852
+ update: true,
5853
+ });
5854
+ this.resetUnsavedInputData();
5855
+ }
5856
+ this.event.emit('input', inputEvent);
5857
+ return;
5842
5858
  }
5843
5859
  this.history.save();
5844
5860
  this.event.emit('input', inputEvent);
5845
5861
  }, 0);
5846
5862
  });
5847
- this.command.event.on('beforeexecute', () => this.commitUnsavedInputData());
5848
5863
  }
5864
+ // Binds events about history.
5849
5865
  bindHistoryEvents() {
5866
+ const executeCommonMethods = (value) => {
5867
+ if (this.fixContent()) {
5868
+ this.history.save({
5869
+ update: true,
5870
+ emitEvent: false,
5871
+ });
5872
+ value = this.getValue();
5873
+ }
5874
+ this.emitStateChangeEvent();
5875
+ this.togglePlaceholderClass(value);
5876
+ this.scrollToCaret();
5877
+ this.event.emit('change', value);
5878
+ };
5850
5879
  this.history.event.on('undo', value => {
5851
5880
  this.renderBoxes();
5852
- this.emitChangeEvent(value);
5881
+ executeCommonMethods(value);
5882
+ this.resetUnsavedInputData();
5853
5883
  });
5854
5884
  this.history.event.on('redo', value => {
5855
5885
  this.renderBoxes();
5856
- this.emitChangeEvent(value);
5886
+ executeCommonMethods(value);
5887
+ this.resetUnsavedInputData();
5857
5888
  });
5858
- this.history.event.on('save', value => {
5889
+ this.history.event.on('save', (value, options) => {
5859
5890
  this.removeBoxGarbage();
5860
- this.emitChangeEvent(value);
5891
+ executeCommonMethods(value);
5892
+ this.selection.sync();
5893
+ if (options.inputType !== 'insertText') {
5894
+ this.resetUnsavedInputData();
5895
+ }
5861
5896
  });
5862
5897
  }
5863
5898
  // Returns a boolean value indicating whether the editor has focus.
@@ -5874,47 +5909,34 @@ class Editor {
5874
5909
  }
5875
5910
  // Fixes wrong content, especially empty tag.
5876
5911
  fixContent() {
5912
+ let changed = false;
5877
5913
  let children = this.container.children();
5878
5914
  for (const child of children) {
5879
5915
  if ((child.isBlock || child.isMark) && child.html() === '') {
5880
5916
  child.remove();
5881
- debug('fixContent(): empty tag was removed');
5917
+ changed = true;
5918
+ debug(`Content fixed: empty tag "${child.name}" was removed`);
5882
5919
  }
5883
5920
  }
5884
5921
  children = this.container.children();
5885
5922
  if (children.length === 0) {
5886
5923
  this.container.html('<p><br /></p>');
5887
5924
  this.selection.range.shrinkAfter(this.container);
5888
- debug('fixContent(): default paragraph was added');
5889
- return;
5925
+ changed = true;
5926
+ debug('Content fixed: default paragraph was added');
5890
5927
  }
5891
- if (children.length === 1) {
5928
+ else if (children.length === 1) {
5892
5929
  const child = children[0];
5893
5930
  if (child.isVoid) {
5894
5931
  const paragraph = query('<p />');
5895
5932
  child.before(paragraph);
5896
5933
  paragraph.append(child);
5897
5934
  this.selection.range.shrinkAfter(paragraph);
5898
- debug('fixContent(): void element was wrapped in paragraph');
5935
+ changed = true;
5936
+ debug(`Content fixed: void element "${child.name}" was wrapped in paragraph`);
5899
5937
  }
5900
5938
  }
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();
5939
+ return changed;
5918
5940
  }
5919
5941
  // Sets default config for a plugin.
5920
5942
  setPluginConfig(name, config) {
@@ -5951,11 +5973,11 @@ class Editor {
5951
5973
  box.render();
5952
5974
  });
5953
5975
  }
5954
- // Sets focus on the editor area.
5976
+ // Sets focus on the editor.
5955
5977
  focus() {
5956
5978
  this.container.focus();
5957
5979
  }
5958
- // Removes focus from the editor area.
5980
+ // Removes focus from the editor.
5959
5981
  blur() {
5960
5982
  this.container.blur();
5961
5983
  }
@@ -5963,6 +5985,9 @@ class Editor {
5963
5985
  scrollToCaret() {
5964
5986
  // Creates an artificial caret that is the same size as the caret at the current caret position.
5965
5987
  const rangeRect = this.selection.range.getRect();
5988
+ if (rangeRect.x === 0 || rangeRect.y === 0) {
5989
+ return;
5990
+ }
5966
5991
  const containerRect = this.container.get(0).getBoundingClientRect();
5967
5992
  const artificialCaret = query('<div class="lake-artificial-caret" />');
5968
5993
  const left = rangeRect.x - containerRect.x;
@@ -5991,7 +6016,7 @@ class Editor {
5991
6016
  }
5992
6017
  artificialCaret.remove();
5993
6018
  }
5994
- // Sets the specified HTML string to the editor area.
6019
+ // Sets the specified value to the editor.
5995
6020
  setValue(value) {
5996
6021
  value = normalizeValue(value);
5997
6022
  const htmlParser = new HTMLParser(value);
@@ -6002,15 +6027,14 @@ class Editor {
6002
6027
  this.renderBoxes();
6003
6028
  this.selection.updateByBookmark();
6004
6029
  }
6005
- // Returns the contents from the editor.
6030
+ // Returns the value of the editor.
6006
6031
  getValue() {
6007
- const bookmark = this.selection.insertBookmark();
6008
- let value = new HTMLParser(this.container).getHTML();
6032
+ const item = this.history.cloneContainer();
6033
+ let value = new HTMLParser(item).getHTML();
6009
6034
  value = denormalizeValue(value);
6010
- this.selection.toBookmark(bookmark);
6011
6035
  return value;
6012
6036
  }
6013
- // Renders an editor area and set default value to it.
6037
+ // Renders an editor area and sets default value to it.
6014
6038
  render() {
6015
6039
  const value = normalizeValue(this.config.value);
6016
6040
  const htmlParser = new HTMLParser(value);
@@ -6025,7 +6049,9 @@ class Editor {
6025
6049
  Editor.plugin.loadAll(this);
6026
6050
  if (!this.readonly) {
6027
6051
  this.selection.updateByBookmark();
6028
- this.history.save();
6052
+ this.history.save({
6053
+ emitEvent: false,
6054
+ });
6029
6055
  }
6030
6056
  this.renderBoxes();
6031
6057
  if (this.toolbar) {
@@ -6035,7 +6061,6 @@ class Editor {
6035
6061
  if (!this.readonly) {
6036
6062
  document.addEventListener('cut', this.cutListener);
6037
6063
  document.addEventListener('paste', this.pasteListener);
6038
- window.addEventListener('beforeunload', this.beforeunloadListener);
6039
6064
  document.addEventListener('selectionchange', this.selectionchangeListener);
6040
6065
  document.addEventListener('click', this.clickListener);
6041
6066
  window.addEventListener('resize', this.resizeListener);
@@ -6043,10 +6068,9 @@ class Editor {
6043
6068
  this.bindHistoryEvents();
6044
6069
  }
6045
6070
  }
6046
- // Destroys a rendered editor.
6071
+ // Destroys the rendered editor.
6047
6072
  unmount() {
6048
6073
  this.event.removeAllListeners();
6049
- this.command.event.removeAllListeners();
6050
6074
  this.history.event.removeAllListeners();
6051
6075
  this.root.empty();
6052
6076
  this.popupContainer.remove();
@@ -6054,7 +6078,6 @@ class Editor {
6054
6078
  if (!this.readonly) {
6055
6079
  document.removeEventListener('cut', this.cutListener);
6056
6080
  document.removeEventListener('paste', this.pasteListener);
6057
- window.removeEventListener('beforeunload', this.beforeunloadListener);
6058
6081
  document.removeEventListener('selectionchange', this.selectionchangeListener);
6059
6082
  document.removeEventListener('click', this.clickListener);
6060
6083
  window.removeEventListener('resize', this.resizeListener);
@@ -7103,7 +7126,6 @@ function appendButtonGroup(box) {
7103
7126
  event.stopPropagation();
7104
7127
  editor.selection.removeBox(box);
7105
7128
  editor.history.save();
7106
- editor.selection.sync();
7107
7129
  });
7108
7130
  videoNode.append(buttonGroupNode);
7109
7131
  }
@@ -7825,7 +7847,6 @@ const imageBox = {
7825
7847
  }
7826
7848
  editor.selection.removeBox(box);
7827
7849
  editor.history.save();
7828
- editor.selection.sync();
7829
7850
  });
7830
7851
  }
7831
7852
  box.event.emit('render');
@@ -7862,7 +7883,6 @@ const boxToolbarItems = [
7862
7883
  }
7863
7884
  editor.selection.removeBox(box);
7864
7885
  editor.history.save();
7865
- editor.selection.sync();
7866
7886
  },
7867
7887
  },
7868
7888
  ];
@@ -8554,7 +8574,6 @@ var list = (editor) => {
8554
8574
  }
8555
8575
  }
8556
8576
  editor.history.save();
8557
- editor.selection.sync();
8558
8577
  },
8559
8578
  });
8560
8579
  editor.container.on('click', event => {
@@ -9166,14 +9185,12 @@ var link = (editor) => {
9166
9185
  const range = editor.selection.range;
9167
9186
  range.setStartAfter(node);
9168
9187
  range.collapseToStart();
9169
- editor.selection.sync();
9170
9188
  editor.history.save();
9171
9189
  },
9172
9190
  onRemove: node => {
9173
9191
  const range = editor.selection.range;
9174
9192
  range.setStartAfter(node);
9175
9193
  range.collapseToStart();
9176
- editor.selection.sync();
9177
9194
  editor.history.save();
9178
9195
  },
9179
9196
  });
@@ -9554,7 +9571,7 @@ function executeMarkCommand(editor, point) {
9554
9571
  // <p>foobold\u200B<focus /></p>,
9555
9572
  // to
9556
9573
  // <p>foo[bold]\u200B<focus /></p>, startOffset = 3, endOffset = 7
9557
- editor.prepareOperation();
9574
+ editor.history.pause();
9558
9575
  const bookmark = selection.insertBookmark();
9559
9576
  const node = bookmark.focus.prev();
9560
9577
  const oldValue = node.text();
@@ -9564,7 +9581,8 @@ function executeMarkCommand(editor, point) {
9564
9581
  range.setEnd(node, offset - (oldValue.length - newValue.length) - 1);
9565
9582
  editor.command.execute(commandName, ...parameters);
9566
9583
  selection.toBookmark(bookmark);
9567
- editor.commitOperation();
9584
+ editor.history.continue();
9585
+ editor.history.save();
9568
9586
  return true;
9569
9587
  }
9570
9588
  }
@@ -9586,16 +9604,14 @@ function spaceKeyExecutesBlockCommand(editor, point) {
9586
9604
  // <p>#<focus />foo</p>
9587
9605
  // to
9588
9606
  // <h1><focus />foo</h1>
9589
- editor.prepareOperation();
9590
9607
  const bookmark = selection.insertBookmark();
9591
9608
  const node = bookmark.focus.prev();
9592
9609
  node.remove();
9593
9610
  const block = bookmark.focus.closestBlock();
9594
9611
  fixEmptyBlock(block);
9595
9612
  selection.range.shrinkAfter(block);
9596
- editor.command.execute(commandName, ...parameters);
9597
9613
  selection.toBookmark(bookmark);
9598
- editor.commitOperation();
9614
+ editor.command.execute(commandName, ...parameters);
9599
9615
  return true;
9600
9616
  }
9601
9617
  }
@@ -9616,12 +9632,10 @@ function enterKeyExecutesBlockCommand(editor, block) {
9616
9632
  // <p>---<focus /></p>
9617
9633
  // to
9618
9634
  // <lake-box type="block" name="hr" focus="end"></lake-box>
9619
- editor.prepareOperation();
9620
9635
  block.empty();
9621
9636
  fixEmptyBlock(block);
9622
9637
  selection.range.shrinkAfter(block);
9623
9638
  editor.command.execute(commandName, ...parameters);
9624
- editor.commitOperation();
9625
9639
  return true;
9626
9640
  }
9627
9641
  }
@@ -9848,6 +9862,14 @@ var shiftEnterKey = (editor) => {
9848
9862
  });
9849
9863
  };
9850
9864
 
9865
+ function removeEmptyMarks(range) {
9866
+ const block = range.getBlocks()[0];
9867
+ if (block && block.isEmpty && block.first().name !== 'br') {
9868
+ block.empty();
9869
+ appendDeepest(block, query('<br />'));
9870
+ range.shrinkAfter(block);
9871
+ }
9872
+ }
9851
9873
  function mergeWithPreviousBlock(editor, block) {
9852
9874
  const range = editor.selection.range;
9853
9875
  let prevBlock = block.prev();
@@ -9878,6 +9900,7 @@ function mergeWithPreviousBlock(editor, block) {
9878
9900
  prevBlock.remove();
9879
9901
  return;
9880
9902
  }
9903
+ removeEmptyMarks(range);
9881
9904
  const bookmark = editor.selection.insertBookmark();
9882
9905
  mergeNodes(prevBlock, block);
9883
9906
  editor.selection.toBookmark(bookmark);
@@ -9890,6 +9913,14 @@ var backspaceKey = (editor) => {
9890
9913
  editor.keystroke.setKeydown('backspace', event => {
9891
9914
  const range = editor.selection.range;
9892
9915
  if (range.isInsideBox) {
9916
+ const boxNode = range.commonAncestor.closest('lake-box');
9917
+ const box = getBox(boxNode);
9918
+ const boxValue = box.value;
9919
+ if (box.name === 'codeBlock' && (boxValue.code === undefined || boxValue.code === '')) {
9920
+ event.preventDefault();
9921
+ editor.selection.removeBox(box);
9922
+ editor.history.save();
9923
+ }
9893
9924
  return;
9894
9925
  }
9895
9926
  editor.fixContent();
@@ -9948,7 +9979,7 @@ var backspaceKey = (editor) => {
9948
9979
  editor.history.save();
9949
9980
  return;
9950
9981
  }
9951
- if (prevNode.isText && prevNode.text().length === 1) {
9982
+ if (prevNode.isText && prevNode.text().length === 1 && prevNode.parent().isBlock) {
9952
9983
  event.preventDefault();
9953
9984
  const block = prevNode.closestBlock();
9954
9985
  range.setStartBefore(prevNode);