cledit refactoring
This commit is contained in:
parent
f0721c9405
commit
d57d2bb969
@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import Prism from 'prismjs';
|
||||
import cledit from '../libs/cledit';
|
||||
import cledit from '../services/cledit';
|
||||
|
||||
export default {
|
||||
props: ['value', 'lang', 'disabled'],
|
||||
|
@ -34,7 +34,7 @@
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import editorSvc from '../services/editorSvc';
|
||||
import cledit from '../libs/cledit';
|
||||
import cledit from '../services/cledit';
|
||||
import store from '../store';
|
||||
import EditorClassApplier from './common/EditorClassApplier';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import cledit from '../../libs/cledit';
|
||||
import cledit from '../../services/cledit';
|
||||
import editorSvc from '../../services/editorSvc';
|
||||
import utils from '../../services/utils';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import cledit from '../../libs/cledit';
|
||||
import cledit from '../../services/cledit';
|
||||
import editorSvc from '../../services/editorSvc';
|
||||
import utils from '../../services/utils';
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
import { mapGetters, mapMutations, mapActions } from 'vuex';
|
||||
import Prism from 'prismjs';
|
||||
import UserImage from '../UserImage';
|
||||
import cledit from '../../libs/cledit';
|
||||
import cledit from '../../services/cledit';
|
||||
import editorSvc from '../../services/editorSvc';
|
||||
import markdownConversionSvc from '../../services/markdownConversionSvc';
|
||||
import utils from '../../services/utils';
|
||||
|
@ -37,15 +37,6 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
let lastTextContent = getTextContent();
|
||||
const highlighter = new cledit.Highlighter(editor);
|
||||
|
||||
let sectionList;
|
||||
|
||||
function parseSections(content, isInit) {
|
||||
sectionList = highlighter.parseSections(content, isInit);
|
||||
editor.$allElements = Array.prototype.slice
|
||||
.call(contentElt.querySelectorAll('.cledit-section *'));
|
||||
return sectionList;
|
||||
}
|
||||
|
||||
/* eslint-disable new-cap */
|
||||
const diffMatchPatch = new DiffMatchPatch();
|
||||
/* eslint-enable new-cap */
|
||||
@ -152,7 +143,11 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
}, 10);
|
||||
|
||||
let watcher;
|
||||
let skipSaveSelection;
|
||||
function checkContentChange(mutations) {
|
||||
if (contentElt.textContent.indexOf('.') >= 0) {
|
||||
debugger;
|
||||
}
|
||||
watcher.noWatch(() => {
|
||||
const removedSections = [];
|
||||
const modifiedSections = [];
|
||||
@ -186,9 +181,13 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
marker.adjustOffset(diffs);
|
||||
});
|
||||
|
||||
if (!skipSaveSelection) {
|
||||
selectionMgr.saveSelectionState();
|
||||
const parsedSections = parseSections(newTextContent);
|
||||
editor.$trigger('contentChanged', newTextContent, diffs, parsedSections);
|
||||
}
|
||||
skipSaveSelection = false;
|
||||
|
||||
const sectionList = highlighter.parseSections(newTextContent);
|
||||
editor.$trigger('contentChanged', newTextContent, diffs, sectionList);
|
||||
if (!ignoreUndo) {
|
||||
undoMgr.addDiffs(lastTextContent, newTextContent, diffs);
|
||||
undoMgr.setDefaultMode('typing');
|
||||
@ -270,12 +269,12 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
|
||||
contentElt.addEventListener('keydown', keydownHandler((evt) => {
|
||||
selectionMgr.saveSelectionState();
|
||||
adjustCursorPosition();
|
||||
|
||||
// Perform keystroke
|
||||
let contentChanging = false;
|
||||
const textContent = getTextContent();
|
||||
const min = Math.min(selectionMgr.selectionStart, selectionMgr.selectionEnd);
|
||||
const max = Math.max(selectionMgr.selectionStart, selectionMgr.selectionEnd);
|
||||
let min = Math.min(selectionMgr.selectionStart, selectionMgr.selectionEnd);
|
||||
let max = Math.max(selectionMgr.selectionStart, selectionMgr.selectionEnd);
|
||||
const state = {
|
||||
before: textContent.slice(0, min),
|
||||
after: textContent.slice(max),
|
||||
@ -286,15 +285,26 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
if (!keystroke.handler(evt, state, editor)) {
|
||||
return false;
|
||||
}
|
||||
editor.setContent(state.before + state.selection + state.after, false, min);
|
||||
const min1 = state.before.length;
|
||||
const max1 = min + state.selection.length;
|
||||
const newContent = state.before + state.selection + state.after;
|
||||
if (newContent !== getTextContent()) {
|
||||
editor.setContent(newContent, false, min);
|
||||
contentChanging = true;
|
||||
skipSaveSelection = true;
|
||||
}
|
||||
min = state.before.length;
|
||||
max = min + state.selection.length;
|
||||
selectionMgr.setSelectionStartEnd(
|
||||
state.isBackwardSelection ? max1 : min1,
|
||||
state.isBackwardSelection ? min : max1,
|
||||
state.isBackwardSelection ? max : min,
|
||||
state.isBackwardSelection ? min : max,
|
||||
!contentChanging, // Expect a restore selection on mutation event
|
||||
);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!contentChanging) {
|
||||
// Optimization to avoid saving selection
|
||||
adjustCursorPosition();
|
||||
}
|
||||
}));
|
||||
|
||||
contentElt.addEventListener('compositionstart', () => {
|
||||
@ -406,8 +416,8 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
}
|
||||
}
|
||||
|
||||
const parsedSections = parseSections(lastTextContent, true);
|
||||
editor.$trigger('contentChanged', lastTextContent, [0, lastTextContent], parsedSections);
|
||||
const sectionList = highlighter.parseSections(lastTextContent, true);
|
||||
editor.$trigger('contentChanged', lastTextContent, [0, lastTextContent], sectionList);
|
||||
if (options.selectionStart !== undefined && options.selectionEnd !== undefined) {
|
||||
editor.setSelection(options.selectionStart, options.selectionEnd);
|
||||
} else {
|
||||
@ -423,4 +433,4 @@ function cledit(contentElt, scrollEltOpt) {
|
||||
return editor;
|
||||
}
|
||||
|
||||
module.exports = cledit;
|
||||
export default cledit;
|
||||
|
@ -11,7 +11,6 @@ function createStyleSheet(document) {
|
||||
}
|
||||
|
||||
function Highlighter(editor) {
|
||||
const self = this;
|
||||
cledit.Utils.createEventHooks(this);
|
||||
|
||||
if (!styleElts.cl_some(styleElt => document.head.contains(styleElt))) {
|
||||
@ -63,7 +62,7 @@ function Highlighter(editor) {
|
||||
}
|
||||
|
||||
this.parseSections = (content, isInit) => {
|
||||
if (this.isComposing) {
|
||||
if (true) {
|
||||
return sectionList;
|
||||
}
|
||||
|
||||
@ -133,14 +132,14 @@ function Highlighter(editor) {
|
||||
sectionList = leftSections.concat(modifiedSections).concat(rightSections);
|
||||
}
|
||||
|
||||
function highlight(section) {
|
||||
const highlight = (section) => {
|
||||
const html = editor.options.sectionHighlighter(section).replace(/\n/g, lfHtml);
|
||||
const sectionElt = document.createElement('div');
|
||||
sectionElt.className = 'cledit-section';
|
||||
sectionElt.innerHTML = html;
|
||||
section.setElement(sectionElt);
|
||||
self.$trigger('sectionHighlighted', section);
|
||||
}
|
||||
this.$trigger('sectionHighlighted', section);
|
||||
};
|
||||
|
||||
const newSectionEltList = document.createDocumentFragment();
|
||||
modifiedSections.cl_each((section) => {
|
||||
@ -183,7 +182,8 @@ function Highlighter(editor) {
|
||||
childNode = nextNode;
|
||||
}
|
||||
this.addTrailingNode();
|
||||
self.$trigger('highlighted');
|
||||
this.$trigger('highlighted');
|
||||
|
||||
if (editor.selectionMgr.hasFocus()) {
|
||||
editor.selectionMgr.restoreSelection();
|
||||
editor.selectionMgr.updateCursorCoordinates();
|
||||
|
@ -6,7 +6,6 @@ function SelectionMgr(editor) {
|
||||
const scrollElt = editor.$scrollElt;
|
||||
cledit.Utils.createEventHooks(this);
|
||||
|
||||
const self = this;
|
||||
let lastSelectionStart = 0;
|
||||
let lastSelectionEnd = 0;
|
||||
this.selectionStart = 0;
|
||||
@ -28,7 +27,7 @@ function SelectionMgr(editor) {
|
||||
return result;
|
||||
};
|
||||
|
||||
this.createRange = function (start, end) {
|
||||
this.createRange = (start, end) => {
|
||||
const range = document.createRange();
|
||||
const startContainer = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start);
|
||||
let endContainer = startContainer;
|
||||
@ -72,14 +71,14 @@ function SelectionMgr(editor) {
|
||||
adjustScroll = false;
|
||||
});
|
||||
|
||||
this.updateCursorCoordinates = function (adjustScrollParam) {
|
||||
this.updateCursorCoordinates = (adjustScrollParam) => {
|
||||
adjustScroll = adjustScroll || adjustScrollParam;
|
||||
debouncedUpdateCursorCoordinates();
|
||||
};
|
||||
|
||||
let oldSelectionRange;
|
||||
|
||||
function checkSelection(selectionRange) {
|
||||
const checkSelection = (selectionRange) => {
|
||||
if (!oldSelectionRange ||
|
||||
oldSelectionRange.startContainer !== selectionRange.startContainer ||
|
||||
oldSelectionRange.startOffset !== selectionRange.startOffset ||
|
||||
@ -87,11 +86,11 @@ function SelectionMgr(editor) {
|
||||
oldSelectionRange.endOffset !== selectionRange.endOffset
|
||||
) {
|
||||
oldSelectionRange = selectionRange;
|
||||
self.$trigger('selectionChanged', self.selectionStart, self.selectionEnd, selectionRange);
|
||||
this.$trigger('selectionChanged', this.selectionStart, this.selectionEnd, selectionRange);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
this.hasFocus = () => contentElt === document.activeElement;
|
||||
|
||||
@ -118,19 +117,22 @@ function SelectionMgr(editor) {
|
||||
};
|
||||
|
||||
const saveLastSelection = debounce(() => {
|
||||
lastSelectionStart = self.selectionStart;
|
||||
lastSelectionEnd = self.selectionEnd;
|
||||
lastSelectionStart = this.selectionStart;
|
||||
lastSelectionEnd = this.selectionEnd;
|
||||
}, 50);
|
||||
|
||||
function setSelection(start = self.selectionStart, end = this.selectionEnd) {
|
||||
self.selectionStart = start < 0 ? 0 : start;
|
||||
self.selectionEnd = end < 0 ? 0 : end;
|
||||
const setSelection = (start = this.selectionStart, end = this.selectionEnd) => {
|
||||
this.selectionStart = start < 0 ? 0 : start;
|
||||
this.selectionEnd = end < 0 ? 0 : end;
|
||||
saveLastSelection();
|
||||
}
|
||||
};
|
||||
|
||||
this.setSelectionStartEnd = (start, end) => {
|
||||
this.setSelectionStartEnd = (start, end, restoreSelection = true) => {
|
||||
setSelection(start, end);
|
||||
return this.hasFocus() && this.restoreSelection();
|
||||
if (restoreSelection && this.hasFocus()) {
|
||||
return this.restoreSelection();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
this.saveSelectionState = (() => {
|
||||
@ -217,7 +219,7 @@ function SelectionMgr(editor) {
|
||||
if (childA === childB) {
|
||||
// This shouldn't be possible
|
||||
throw module.createError('comparePoints got to case 4 and childA and childB are the same!');
|
||||
} else {
|
||||
}
|
||||
n = root.firstChild;
|
||||
while (n) {
|
||||
if (n === childA) {
|
||||
@ -227,172 +229,189 @@ function SelectionMgr(editor) {
|
||||
}
|
||||
n = n.nextSibling;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function save() {
|
||||
const save = () => {
|
||||
let result;
|
||||
if (self.hasFocus()) {
|
||||
const selectionStart = self.selectionStart;
|
||||
const selectionEnd = self.selectionEnd;
|
||||
if (this.hasFocus()) {
|
||||
let selectionStart = this.selectionStart;
|
||||
let selectionEnd = this.selectionEnd;
|
||||
const selection = window.getSelection();
|
||||
if (selection.rangeCount > 0) {
|
||||
const selectionRange = selection.getRangeAt(0);
|
||||
const node = selectionRange.startContainer;
|
||||
if ((contentElt.compareDocumentPosition(node) & window.Node.DOCUMENT_POSITION_CONTAINED_BY) || contentElt === node) {
|
||||
var offset = selectionRange.startOffset
|
||||
let node = selectionRange.startContainer;
|
||||
// eslint-disable-next-line no-bitwise
|
||||
if ((contentElt.compareDocumentPosition(node)
|
||||
& window.Node.DOCUMENT_POSITION_CONTAINED_BY)
|
||||
|| contentElt === node
|
||||
) {
|
||||
let offset = selectionRange.startOffset;
|
||||
if (node.firstChild && offset > 0) {
|
||||
node = node.childNodes[offset - 1]
|
||||
offset = node.textContent.length
|
||||
node = node.childNodes[offset - 1];
|
||||
offset = node.textContent.length;
|
||||
}
|
||||
var container = node
|
||||
let container = node;
|
||||
while (node !== contentElt) {
|
||||
while ((node = node.previousSibling)) {
|
||||
offset += (node.textContent || '').length
|
||||
node = node.previousSibling;
|
||||
while (node) {
|
||||
offset += (node.textContent || '').length;
|
||||
node = node.previousSibling;
|
||||
}
|
||||
node = container = container.parentNode
|
||||
node = container.parentNode;
|
||||
container = node;
|
||||
}
|
||||
var selectionText = selectionRange + ''
|
||||
let selectionText = `${selectionRange}`;
|
||||
// Fix end of line when only br is selected
|
||||
var brElt = selectionRange.endContainer.firstChild
|
||||
const brElt = selectionRange.endContainer.firstChild;
|
||||
if (brElt && brElt.tagName === 'BR' && selectionRange.endOffset === 1) {
|
||||
selectionText += '\n'
|
||||
selectionText += '\n';
|
||||
}
|
||||
if (comparePoints(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset) === 1) {
|
||||
selectionStart = offset + selectionText.length
|
||||
selectionEnd = offset
|
||||
if (comparePoints(
|
||||
selection.anchorNode,
|
||||
selection.anchorOffset,
|
||||
selection.focusNode,
|
||||
selection.focusOffset) === 1
|
||||
) {
|
||||
selectionStart = offset + selectionText.length;
|
||||
selectionEnd = offset;
|
||||
} else {
|
||||
selectionStart = offset
|
||||
selectionEnd = offset + selectionText.length
|
||||
selectionStart = offset;
|
||||
selectionEnd = offset + selectionText.length;
|
||||
}
|
||||
|
||||
if (selectionStart === selectionEnd && selectionStart === editor.getContent().length) {
|
||||
// If cursor is after the trailingNode
|
||||
selectionStart = --selectionEnd
|
||||
result = self.setSelectionStartEnd(selectionStart, selectionEnd)
|
||||
selectionEnd -= 1;
|
||||
selectionStart = selectionEnd;
|
||||
result = this.setSelectionStartEnd(selectionStart, selectionEnd);
|
||||
} else {
|
||||
setSelection(selectionStart, selectionEnd)
|
||||
result = checkSelection(selectionRange)
|
||||
result = result || lastSelectionStart !== self.selectionStart // selectionRange doesn't change when selection is at the start of a section
|
||||
setSelection(selectionStart, selectionEnd);
|
||||
result = checkSelection(selectionRange);
|
||||
// selectionRange doesn't change when selection is at the start of a section
|
||||
result = result || lastSelectionStart !== this.selectionStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
function saveCheckChange() {
|
||||
return save() && (lastSelectionStart !== self.selectionStart || lastSelectionEnd !== self.selectionEnd)
|
||||
}
|
||||
const saveCheckChange = () => save() && (
|
||||
lastSelectionStart !== this.selectionStart || lastSelectionEnd !== this.selectionEnd);
|
||||
|
||||
var nextTickAdjustScroll = false
|
||||
var debouncedSave = debounce(function () {
|
||||
self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll)
|
||||
// In some cases we have to wait a little longer to see the selection change (Cmd+A on Chrome OSX)
|
||||
longerDebouncedSave()
|
||||
})
|
||||
var longerDebouncedSave = debounce(function () {
|
||||
self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll)
|
||||
nextTickAdjustScroll = false
|
||||
}, 10)
|
||||
let nextTickAdjustScroll = false;
|
||||
const longerDebouncedSave = debounce(() => {
|
||||
this.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll);
|
||||
nextTickAdjustScroll = false;
|
||||
}, 10);
|
||||
const debouncedSave = debounce(() => {
|
||||
this.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll);
|
||||
// In some cases we have to wait a little longer to see the
|
||||
// selection change (Cmd+A on Chrome OSX)
|
||||
longerDebouncedSave();
|
||||
});
|
||||
|
||||
return function (debounced, adjustScroll, forceAdjustScroll) {
|
||||
return (debounced, adjustScrollParam, forceAdjustScroll) => {
|
||||
if (forceAdjustScroll) {
|
||||
lastSelectionStart = undefined
|
||||
lastSelectionEnd = undefined
|
||||
lastSelectionStart = undefined;
|
||||
lastSelectionEnd = undefined;
|
||||
}
|
||||
if (debounced) {
|
||||
nextTickAdjustScroll = nextTickAdjustScroll || adjustScroll
|
||||
return debouncedSave()
|
||||
nextTickAdjustScroll = nextTickAdjustScroll || adjustScrollParam;
|
||||
debouncedSave();
|
||||
} else {
|
||||
save()
|
||||
save();
|
||||
}
|
||||
}
|
||||
})()
|
||||
};
|
||||
})();
|
||||
|
||||
this.getSelectedText = function () {
|
||||
var min = Math.min(this.selectionStart, this.selectionEnd)
|
||||
var max = Math.max(this.selectionStart, this.selectionEnd)
|
||||
return editor.getContent().substring(min, max)
|
||||
}
|
||||
this.getSelectedText = () => {
|
||||
const min = Math.min(this.selectionStart, this.selectionEnd);
|
||||
const max = Math.max(this.selectionStart, this.selectionEnd);
|
||||
return editor.getContent().substring(min, max);
|
||||
};
|
||||
|
||||
this.getCoordinates = function (inputOffset, container, offsetInContainer) {
|
||||
this.getCoordinates = (inputOffset, containerParam, offsetInContainerParam) => {
|
||||
let container = containerParam;
|
||||
let offsetInContainer = offsetInContainerParam;
|
||||
if (!container) {
|
||||
var offset = this.findContainer(inputOffset)
|
||||
container = offset.container
|
||||
offsetInContainer = offset.offsetInContainer
|
||||
const offset = this.findContainer(inputOffset);
|
||||
container = offset.container;
|
||||
offsetInContainer = offset.offsetInContainer;
|
||||
}
|
||||
var containerElt = container
|
||||
let containerElt = container;
|
||||
if (!containerElt.hasChildNodes()) {
|
||||
containerElt = container.parentNode
|
||||
containerElt = container.parentNode;
|
||||
}
|
||||
var isInvisible = false
|
||||
var index = editor.$allElements.indexOf(containerElt)
|
||||
while (containerElt.offsetHeight === 0 && index > 0) {
|
||||
isInvisible = true
|
||||
containerElt = editor.$allElements[--index]
|
||||
}
|
||||
var rect
|
||||
var contentRect
|
||||
var left = 'left'
|
||||
if (isInvisible || container.textContent === '\n') {
|
||||
rect = containerElt.getBoundingClientRect()
|
||||
let isInvisible = false;
|
||||
while (containerElt.offsetHeight === 0) {
|
||||
isInvisible = true;
|
||||
if (containerElt.previousSibling) {
|
||||
containerElt = containerElt.previousSibling;
|
||||
} else {
|
||||
var selectedChar = editor.getContent()[inputOffset]
|
||||
var startOffset = {
|
||||
container: container,
|
||||
offsetInContainer: offsetInContainer
|
||||
containerElt = containerElt.parentNode;
|
||||
}
|
||||
var endOffset = {
|
||||
container: container,
|
||||
offsetInContainer: offsetInContainer
|
||||
}
|
||||
let rect;
|
||||
let left = 'left';
|
||||
if (isInvisible || container.textContent === '\n') {
|
||||
rect = containerElt.getBoundingClientRect();
|
||||
} else {
|
||||
const selectedChar = editor.getContent()[inputOffset];
|
||||
let startOffset = {
|
||||
container,
|
||||
offsetInContainer,
|
||||
};
|
||||
let endOffset = {
|
||||
container,
|
||||
offsetInContainer,
|
||||
};
|
||||
if (inputOffset > 0 && (selectedChar === undefined || selectedChar === '\n')) {
|
||||
left = 'right'
|
||||
left = 'right';
|
||||
if (startOffset.offsetInContainer === 0) {
|
||||
// Need to calculate offset-1
|
||||
startOffset = inputOffset - 1
|
||||
startOffset = inputOffset - 1;
|
||||
} else {
|
||||
startOffset.offsetInContainer -= 1
|
||||
startOffset.offsetInContainer -= 1;
|
||||
}
|
||||
} else {
|
||||
if (endOffset.offsetInContainer === container.textContent.length) {
|
||||
} else if (endOffset.offsetInContainer === container.textContent.length) {
|
||||
// Need to calculate offset+1
|
||||
endOffset = inputOffset + 1
|
||||
endOffset = inputOffset + 1;
|
||||
} else {
|
||||
endOffset.offsetInContainer += 1
|
||||
endOffset.offsetInContainer += 1;
|
||||
}
|
||||
const range = this.createRange(startOffset, endOffset);
|
||||
rect = range.getBoundingClientRect();
|
||||
}
|
||||
var range = this.createRange(startOffset, endOffset)
|
||||
rect = range.getBoundingClientRect()
|
||||
}
|
||||
contentRect = contentElt.getBoundingClientRect()
|
||||
const contentRect = contentElt.getBoundingClientRect();
|
||||
return {
|
||||
top: Math.round(rect.top - contentRect.top + contentElt.scrollTop),
|
||||
top: Math.round((rect.top - contentRect.top) + contentElt.scrollTop),
|
||||
height: Math.round(rect.height),
|
||||
left: Math.round(rect[left] - contentRect.left + contentElt.scrollLeft)
|
||||
}
|
||||
}
|
||||
left: Math.round((rect[left] - contentRect.left) + contentElt.scrollLeft),
|
||||
};
|
||||
};
|
||||
|
||||
this.getClosestWordOffset = function (offset) {
|
||||
var offsetStart = 0
|
||||
var offsetEnd = 0
|
||||
var nextOffset = 0
|
||||
editor.getContent().split(/\s/).cl_some(function (word) {
|
||||
this.getClosestWordOffset = (offset) => {
|
||||
let offsetStart = 0;
|
||||
let offsetEnd = 0;
|
||||
let nextOffset = 0;
|
||||
editor.getContent().split(/\s/).cl_some((word) => {
|
||||
if (word) {
|
||||
offsetStart = nextOffset
|
||||
offsetEnd = nextOffset + word.length
|
||||
offsetStart = nextOffset;
|
||||
offsetEnd = nextOffset + word.length;
|
||||
if (offsetEnd > offset) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
nextOffset += word.length + 1
|
||||
})
|
||||
nextOffset += word.length + 1;
|
||||
return false;
|
||||
});
|
||||
return {
|
||||
start: offsetStart,
|
||||
end: offsetEnd
|
||||
}
|
||||
}
|
||||
end: offsetEnd,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
cledit.SelectionMgr = SelectionMgr
|
||||
cledit.SelectionMgr = SelectionMgr;
|
||||
|
@ -1,178 +1,176 @@
|
||||
var DiffMatchPatch = require('diff-match-patch');
|
||||
var cledit = require('./cleditCore')
|
||||
import DiffMatchPatch from 'diff-match-patch';
|
||||
import cledit from './cleditCore';
|
||||
|
||||
function UndoMgr(editor) {
|
||||
cledit.Utils.createEventHooks(this)
|
||||
cledit.Utils.createEventHooks(this);
|
||||
|
||||
/* eslint-disable new-cap */
|
||||
var diffMatchPatch = new DiffMatchPatch()
|
||||
const diffMatchPatch = new DiffMatchPatch();
|
||||
/* eslint-enable new-cap */
|
||||
|
||||
var self = this
|
||||
var selectionMgr
|
||||
var undoStack = []
|
||||
var redoStack = []
|
||||
var currentState
|
||||
var previousPatches = []
|
||||
var currentPatches = []
|
||||
var debounce = cledit.Utils.debounce
|
||||
const self = this;
|
||||
let selectionMgr;
|
||||
const undoStack = [];
|
||||
const redoStack = [];
|
||||
let currentState;
|
||||
let previousPatches = [];
|
||||
let currentPatches = [];
|
||||
const debounce = cledit.Utils.debounce;
|
||||
|
||||
self.options = {
|
||||
this.options = {
|
||||
undoStackMaxSize: 200,
|
||||
bufferStateUntilIdle: 1000,
|
||||
patchHandler: {
|
||||
makePatches: function (oldContent, newContent, diffs) {
|
||||
return diffMatchPatch.patch_make(oldContent, diffs)
|
||||
makePatches(oldContent, newContent, diffs) {
|
||||
return diffMatchPatch.patch_make(oldContent, diffs);
|
||||
},
|
||||
applyPatches: function (patches, content) {
|
||||
return diffMatchPatch.patch_apply(patches, content)[0]
|
||||
applyPatches(patches, content) {
|
||||
return diffMatchPatch.patch_apply(patches, content)[0];
|
||||
},
|
||||
reversePatches: function (patches) {
|
||||
patches = diffMatchPatch.patch_deepCopy(patches).reverse()
|
||||
patches.cl_each(function (patch) {
|
||||
patch.diffs.cl_each(function (diff) {
|
||||
diff[0] = -diff[0]
|
||||
})
|
||||
})
|
||||
return patches
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function State() { }
|
||||
reversePatches(patches) {
|
||||
const reversedPatches = diffMatchPatch.patch_deepCopy(patches).reverse();
|
||||
reversedPatches.cl_each((patch) => {
|
||||
patch.diffs.cl_each((diff) => {
|
||||
diff[0] = -diff[0];
|
||||
});
|
||||
});
|
||||
return reversedPatches;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let stateMgr;
|
||||
function StateMgr() {
|
||||
var currentTime, lastTime
|
||||
var lastMode
|
||||
let currentTime;
|
||||
let lastTime;
|
||||
let lastMode;
|
||||
|
||||
this.isBufferState = function () {
|
||||
currentTime = Date.now()
|
||||
this.isBufferState = () => {
|
||||
currentTime = Date.now();
|
||||
return this.currentMode !== 'single' &&
|
||||
this.currentMode === lastMode &&
|
||||
currentTime - lastTime < self.options.bufferStateUntilIdle
|
||||
currentTime - lastTime < self.options.bufferStateUntilIdle;
|
||||
};
|
||||
|
||||
this.setDefaultMode = (mode) => {
|
||||
this.currentMode = this.currentMode || mode;
|
||||
};
|
||||
|
||||
this.resetMode = () => {
|
||||
stateMgr.currentMode = undefined;
|
||||
lastMode = undefined;
|
||||
};
|
||||
|
||||
this.saveMode = () => {
|
||||
lastMode = this.currentMode;
|
||||
this.currentMode = undefined;
|
||||
lastTime = currentTime;
|
||||
};
|
||||
}
|
||||
|
||||
this.setDefaultMode = function (mode) {
|
||||
this.currentMode = this.currentMode || mode
|
||||
class State {
|
||||
addToUndoStack() {
|
||||
undoStack.push(this);
|
||||
this.patches = previousPatches;
|
||||
previousPatches = [];
|
||||
}
|
||||
|
||||
this.resetMode = function () {
|
||||
stateMgr.currentMode = undefined
|
||||
lastMode = undefined
|
||||
}
|
||||
|
||||
this.saveMode = function () {
|
||||
lastMode = this.currentMode
|
||||
this.currentMode = undefined
|
||||
lastTime = currentTime
|
||||
addToRedoStack() {
|
||||
redoStack.push(this);
|
||||
this.patches = previousPatches;
|
||||
previousPatches = [];
|
||||
}
|
||||
}
|
||||
|
||||
function addToStack(stack) {
|
||||
return function () {
|
||||
stack.push(this)
|
||||
this.patches = previousPatches
|
||||
previousPatches = []
|
||||
}
|
||||
}
|
||||
stateMgr = new StateMgr();
|
||||
this.setCurrentMode = (mode) => {
|
||||
stateMgr.currentMode = mode;
|
||||
};
|
||||
this.setDefaultMode = stateMgr.setDefaultMode.cl_bind(stateMgr);
|
||||
|
||||
State.prototype.addToUndoStack = addToStack(undoStack)
|
||||
State.prototype.addToRedoStack = addToStack(redoStack)
|
||||
|
||||
var stateMgr = new StateMgr()
|
||||
this.setCurrentMode = function (mode) {
|
||||
stateMgr.currentMode = mode
|
||||
}
|
||||
this.setDefaultMode = stateMgr.setDefaultMode.cl_bind(stateMgr)
|
||||
|
||||
this.addDiffs = function (oldContent, newContent, diffs) {
|
||||
var patches = self.options.patchHandler.makePatches(oldContent, newContent, diffs)
|
||||
currentPatches.push.apply(currentPatches, patches)
|
||||
}
|
||||
this.addDiffs = (oldContent, newContent, diffs) => {
|
||||
const patches = this.options.patchHandler.makePatches(oldContent, newContent, diffs);
|
||||
patches.cl_each(patch => currentPatches.push(patch));
|
||||
};
|
||||
|
||||
function saveCurrentPatches() {
|
||||
// Move currentPatches into previousPatches
|
||||
Array.prototype.push.apply(previousPatches, currentPatches)
|
||||
currentPatches = []
|
||||
Array.prototype.push.apply(previousPatches, currentPatches);
|
||||
currentPatches = [];
|
||||
}
|
||||
|
||||
this.saveState = debounce(function () {
|
||||
redoStack.length = 0
|
||||
this.saveState = debounce(() => {
|
||||
redoStack.length = 0;
|
||||
if (!stateMgr.isBufferState()) {
|
||||
currentState.addToUndoStack()
|
||||
currentState.addToUndoStack();
|
||||
|
||||
// Limit the size of the stack
|
||||
while (undoStack.length > self.options.undoStackMaxSize) {
|
||||
undoStack.shift()
|
||||
while (undoStack.length > this.options.undoStackMaxSize) {
|
||||
undoStack.shift();
|
||||
}
|
||||
}
|
||||
saveCurrentPatches()
|
||||
currentState = new State()
|
||||
stateMgr.saveMode()
|
||||
self.$trigger('undoStateChange')
|
||||
})
|
||||
saveCurrentPatches();
|
||||
currentState = new State();
|
||||
stateMgr.saveMode();
|
||||
this.$trigger('undoStateChange');
|
||||
});
|
||||
|
||||
this.canUndo = function () {
|
||||
return !!undoStack.length
|
||||
}
|
||||
this.canUndo = () => !!undoStack.length;
|
||||
this.canRedo = () => !!redoStack.length;
|
||||
|
||||
this.canRedo = function () {
|
||||
return !!redoStack.length
|
||||
}
|
||||
|
||||
function restoreState(patches, isForward) {
|
||||
const restoreState = (patchesParam, isForward) => {
|
||||
let patches = patchesParam;
|
||||
// Update editor
|
||||
var content = editor.getContent()
|
||||
const content = editor.getContent();
|
||||
if (!isForward) {
|
||||
patches = self.options.patchHandler.reversePatches(patches)
|
||||
patches = this.options.patchHandler.reversePatches(patches);
|
||||
}
|
||||
|
||||
var newContent = self.options.patchHandler.applyPatches(patches, content)
|
||||
var newContentText = newContent.text || newContent
|
||||
var range = editor.setContent(newContentText, true)
|
||||
var selection = newContent.selection || {
|
||||
const newContent = this.options.patchHandler.applyPatches(patches, content);
|
||||
const newContentText = newContent.text || newContent;
|
||||
const range = editor.setContent(newContentText, true);
|
||||
const selection = newContent.selection || {
|
||||
start: range.end,
|
||||
end: range.end
|
||||
}
|
||||
end: range.end,
|
||||
};
|
||||
|
||||
selectionMgr.setSelectionStartEnd(selection.start, selection.end)
|
||||
selectionMgr.updateCursorCoordinates(true)
|
||||
selectionMgr.setSelectionStartEnd(selection.start, selection.end);
|
||||
selectionMgr.updateCursorCoordinates(true);
|
||||
|
||||
stateMgr.resetMode()
|
||||
self.$trigger('undoStateChange')
|
||||
editor.adjustCursorPosition()
|
||||
}
|
||||
stateMgr.resetMode();
|
||||
this.$trigger('undoStateChange');
|
||||
editor.adjustCursorPosition();
|
||||
};
|
||||
|
||||
this.undo = function () {
|
||||
var state = undoStack.pop()
|
||||
this.undo = () => {
|
||||
const state = undoStack.pop();
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
saveCurrentPatches()
|
||||
currentState.addToRedoStack()
|
||||
restoreState(currentState.patches)
|
||||
previousPatches = state.patches
|
||||
currentState = state
|
||||
return;
|
||||
}
|
||||
saveCurrentPatches();
|
||||
currentState.addToRedoStack();
|
||||
restoreState(currentState.patches);
|
||||
previousPatches = state.patches;
|
||||
currentState = state;
|
||||
};
|
||||
|
||||
this.redo = function () {
|
||||
var state = redoStack.pop()
|
||||
this.redo = () => {
|
||||
const state = redoStack.pop();
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
currentState.addToUndoStack()
|
||||
restoreState(state.patches, true)
|
||||
previousPatches = state.patches
|
||||
currentState = state
|
||||
return;
|
||||
}
|
||||
currentState.addToUndoStack();
|
||||
restoreState(state.patches, true);
|
||||
previousPatches = state.patches;
|
||||
currentState = state;
|
||||
};
|
||||
|
||||
this.init = function (options) {
|
||||
self.options.cl_extend(options || {})
|
||||
selectionMgr = editor.selectionMgr
|
||||
this.init = (options) => {
|
||||
this.options.cl_extend(options || {});
|
||||
selectionMgr = editor.selectionMgr;
|
||||
if (!currentState) {
|
||||
currentState = new State()
|
||||
}
|
||||
currentState = new State();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cledit.UndoMgr = UndoMgr
|
||||
cledit.UndoMgr = UndoMgr;
|
||||
|
@ -1,123 +1,128 @@
|
||||
var cledit = require('./cleditCore')
|
||||
import cledit from './cleditCore';
|
||||
|
||||
var Utils = {
|
||||
const Utils = {
|
||||
isGecko: 'MozAppearance' in document.documentElement.style,
|
||||
isWebkit: 'WebkitAppearance' in document.documentElement.style,
|
||||
isMsie: 'msTransform' in document.documentElement.style,
|
||||
isMac: navigator.userAgent.indexOf('Mac OS X') !== -1
|
||||
}
|
||||
isMac: navigator.userAgent.indexOf('Mac OS X') !== -1,
|
||||
};
|
||||
|
||||
// Faster than setTimeout(0). Credit: https://github.com/stefanpenner/es6-promise
|
||||
Utils.defer = (function () {
|
||||
var queue = new Array(1000)
|
||||
var queueLength = 0
|
||||
Utils.defer = (() => {
|
||||
const queue = new Array(1000);
|
||||
let queueLength = 0;
|
||||
function flush() {
|
||||
for (var i = 0; i < queueLength; i++) {
|
||||
for (let i = 0; i < queueLength; i += 1) {
|
||||
try {
|
||||
queue[i]()
|
||||
queue[i]();
|
||||
} catch (e) {
|
||||
console.error(e.message, e.stack)
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e.message, e.stack);
|
||||
}
|
||||
queue[i] = undefined
|
||||
queue[i] = undefined;
|
||||
}
|
||||
queueLength = 0
|
||||
queueLength = 0;
|
||||
}
|
||||
|
||||
var iterations = 0
|
||||
var observer = new window.MutationObserver(flush)
|
||||
var node = document.createTextNode('')
|
||||
observer.observe(node, { characterData: true })
|
||||
let iterations = 0;
|
||||
const observer = new window.MutationObserver(flush);
|
||||
const node = document.createTextNode('');
|
||||
observer.observe(node, { characterData: true });
|
||||
|
||||
return function (fn) {
|
||||
queue[queueLength++] = fn
|
||||
return (fn) => {
|
||||
queue[queueLength] = fn;
|
||||
queueLength += 1;
|
||||
if (queueLength === 1) {
|
||||
node.data = (iterations = ++iterations % 2)
|
||||
iterations = (iterations + 1) % 2;
|
||||
node.data = iterations;
|
||||
}
|
||||
}
|
||||
})()
|
||||
};
|
||||
})();
|
||||
|
||||
Utils.debounce = function (func, wait) {
|
||||
var timeoutId, isExpected
|
||||
Utils.debounce = (func, wait) => {
|
||||
let timeoutId;
|
||||
let isExpected;
|
||||
return wait
|
||||
? function () {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(func, wait)
|
||||
? () => {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(func, wait);
|
||||
}
|
||||
: function () {
|
||||
: () => {
|
||||
if (!isExpected) {
|
||||
isExpected = true
|
||||
Utils.defer(function () {
|
||||
isExpected = false
|
||||
func()
|
||||
})
|
||||
isExpected = true;
|
||||
Utils.defer(() => {
|
||||
isExpected = false;
|
||||
func();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Utils.createEventHooks = function (object) {
|
||||
var listenerMap = Object.create(null)
|
||||
object.$trigger = function (eventType) {
|
||||
var listeners = listenerMap[eventType]
|
||||
Utils.createEventHooks = (object) => {
|
||||
const listenerMap = Object.create(null);
|
||||
object.$trigger = (eventType, ...args) => {
|
||||
const listeners = listenerMap[eventType];
|
||||
if (listeners) {
|
||||
var args = Array.prototype.slice.call(arguments, 1)
|
||||
listeners.cl_each(function (listener) {
|
||||
listeners.cl_each((listener) => {
|
||||
try {
|
||||
listener.apply(object, args)
|
||||
listener.apply(object, args);
|
||||
} catch (e) {
|
||||
console.error(e.message, e.stack)
|
||||
console.error(e.message, e.stack);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
object.on = function (eventType, listener) {
|
||||
var listeners = listenerMap[eventType]
|
||||
};
|
||||
object.on = (eventType, listener) => {
|
||||
let listeners = listenerMap[eventType];
|
||||
if (!listeners) {
|
||||
listeners = []
|
||||
listenerMap[eventType] = listeners
|
||||
listeners = [];
|
||||
listenerMap[eventType] = listeners;
|
||||
}
|
||||
listeners.push(listener)
|
||||
}
|
||||
object.off = function (eventType, listener) {
|
||||
var listeners = listenerMap[eventType]
|
||||
listeners.push(listener);
|
||||
};
|
||||
object.off = (eventType, listener) => {
|
||||
const listeners = listenerMap[eventType];
|
||||
if (listeners) {
|
||||
var index = listeners.indexOf(listener)
|
||||
if (~index) {
|
||||
listeners.splice(index, 1)
|
||||
const index = listeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Utils.findContainer = function (elt, offset) {
|
||||
var containerOffset = 0
|
||||
var container
|
||||
Utils.findContainer = (elt, offset) => {
|
||||
let containerOffset = 0;
|
||||
let container;
|
||||
let child = elt;
|
||||
do {
|
||||
container = elt
|
||||
elt = elt.firstChild
|
||||
if (elt) {
|
||||
container = child;
|
||||
child = child.firstChild;
|
||||
if (child) {
|
||||
do {
|
||||
var len = elt.textContent.length
|
||||
const len = child.textContent.length;
|
||||
if (containerOffset <= offset && containerOffset + len > offset) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
containerOffset += len
|
||||
} while ((elt = elt.nextSibling))
|
||||
containerOffset += len;
|
||||
child = child.nextSibling;
|
||||
} while (child);
|
||||
}
|
||||
} while (elt && elt.firstChild && elt.nodeType !== 3)
|
||||
} while (child && child.firstChild && child.nodeType !== 3);
|
||||
|
||||
if (elt) {
|
||||
if (child) {
|
||||
return {
|
||||
container: elt,
|
||||
offsetInContainer: offset - containerOffset
|
||||
}
|
||||
container: child,
|
||||
offsetInContainer: offset - containerOffset,
|
||||
};
|
||||
}
|
||||
while (container.lastChild) {
|
||||
container = container.lastChild
|
||||
container = container.lastChild;
|
||||
}
|
||||
return {
|
||||
container: container,
|
||||
offsetInContainer: container.nodeType === 3 ? container.textContent.length : 0
|
||||
}
|
||||
}
|
||||
container,
|
||||
offsetInContainer: container.nodeType === 3 ? container.textContent.length : 0,
|
||||
};
|
||||
};
|
||||
|
||||
cledit.Utils = Utils
|
||||
cledit.Utils = Utils;
|
||||
|
@ -1,33 +1,34 @@
|
||||
var cledit = require('./cleditCore')
|
||||
import cledit from './cleditCore';
|
||||
|
||||
function Watcher(editor, listener) {
|
||||
this.isWatching = false
|
||||
var contentObserver
|
||||
this.startWatching = function () {
|
||||
this.stopWatching()
|
||||
this.isWatching = true
|
||||
contentObserver = new window.MutationObserver(listener)
|
||||
this.isWatching = false;
|
||||
let contentObserver;
|
||||
this.startWatching = () => {
|
||||
this.stopWatching();
|
||||
this.isWatching = true;
|
||||
contentObserver = new window.MutationObserver(listener);
|
||||
contentObserver.observe(editor.$contentElt, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
})
|
||||
}
|
||||
this.stopWatching = function () {
|
||||
characterData: true,
|
||||
});
|
||||
};
|
||||
this.stopWatching = () => {
|
||||
if (contentObserver) {
|
||||
contentObserver.disconnect()
|
||||
contentObserver = undefined
|
||||
contentObserver.disconnect();
|
||||
contentObserver = undefined;
|
||||
}
|
||||
this.isWatching = false
|
||||
}
|
||||
this.noWatch = function (cb) {
|
||||
this.isWatching = false;
|
||||
};
|
||||
this.noWatch = (cb) => {
|
||||
if (this.isWatching === true) {
|
||||
this.stopWatching()
|
||||
cb()
|
||||
return this.startWatching()
|
||||
}
|
||||
cb()
|
||||
this.stopWatching();
|
||||
cb();
|
||||
this.startWatching();
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cledit.Watcher = Watcher
|
||||
cledit.Watcher = Watcher;
|
||||
|
@ -2,7 +2,7 @@ import Vue from 'vue';
|
||||
import DiffMatchPatch from 'diff-match-patch';
|
||||
import Prism from 'prismjs';
|
||||
import markdownItPandocRenderer from 'markdown-it-pandoc-renderer';
|
||||
import cledit from '../libs/cledit';
|
||||
import cledit from './cledit';
|
||||
import pagedown from '../libs/pagedown';
|
||||
import htmlSanitizer from '../libs/htmlSanitizer';
|
||||
import markdownConversionSvc from './markdownConversionSvc';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import DiffMatchPatch from 'diff-match-patch';
|
||||
import cledit from '../libs/cledit';
|
||||
import cledit from './cledit';
|
||||
import utils from './utils';
|
||||
import diffUtils from './diffUtils';
|
||||
import store from '../store';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import DiffMatchPatch from 'diff-match-patch';
|
||||
import cledit from '../libs/cledit';
|
||||
import cledit from './cledit';
|
||||
import animationSvc from './animationSvc';
|
||||
import store from '../store';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import cledit from '../../libs/cledit';
|
||||
import cledit from '../cledit';
|
||||
import editorSvc from '../editorSvc';
|
||||
|
||||
const Keystroke = cledit.Keystroke;
|
||||
|
@ -2,7 +2,7 @@ import DiffMatchPatch from 'diff-match-patch';
|
||||
import moduleTemplate from './moduleTemplate';
|
||||
import empty from '../data/emptyContent';
|
||||
import utils from '../services/utils';
|
||||
import cledit from '../libs/cledit';
|
||||
import cledit from '../services/cledit';
|
||||
|
||||
const diffMatchPatch = new DiffMatchPatch();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import createLogger from 'vuex/dist/logger';
|
||||
// import createLogger from 'vuex/dist/logger';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import utils from '../services/utils';
|
||||
@ -120,7 +120,7 @@ const store = new Vuex.Store({
|
||||
},
|
||||
},
|
||||
strict: debug,
|
||||
plugins: debug ? [createLogger()] : [],
|
||||
// plugins: debug ? [createLogger()] : [],
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user