mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-05 15:37:03 +03:00
Cleanup, fix and improve some Squire handling
This commit is contained in:
parent
4606ddf182
commit
d21b7e754f
1 changed files with 149 additions and 105 deletions
254
vendors/squire/build/squire-raw.js
vendored
254
vendors/squire/build/squire-raw.js
vendored
|
|
@ -414,10 +414,10 @@ const
|
||||||
if (isTextNode(child)) {
|
if (isTextNode(child)) {
|
||||||
prev.appendData(child.data);
|
prev.appendData(child.data);
|
||||||
} else {
|
} else {
|
||||||
frags.push(empty(child));
|
frags.unshift(empty(child));
|
||||||
}
|
}
|
||||||
} else if (isElement(child)) {
|
} else if (isElement(child)) {
|
||||||
child.append(...frags.reverse());
|
child.append(...frags);
|
||||||
frags = [];
|
frags = [];
|
||||||
_mergeInlines(child, fakeRange);
|
_mergeInlines(child, fakeRange);
|
||||||
}
|
}
|
||||||
|
|
@ -458,9 +458,9 @@ const
|
||||||
mergeInlines(block, range);
|
mergeInlines(block, range);
|
||||||
},
|
},
|
||||||
mergeContainers = (node, root) => {
|
mergeContainers = (node, root) => {
|
||||||
const prev = node.previousSibling,
|
const prev = node.previousSibling;
|
||||||
first = node.firstChild,
|
const first = node.firstChild;
|
||||||
isListItem = node.nodeName === "LI";
|
const isListItem = node.nodeName === "LI";
|
||||||
// Do not merge LIs, unless it only contains a UL
|
// Do not merge LIs, unless it only contains a UL
|
||||||
if (isListItem && (!first || !listNodeNames.has(first.nodeName))) {
|
if (isListItem && (!first || !listNodeNames.has(first.nodeName))) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -579,11 +579,11 @@ const
|
||||||
STRIKE: replaceWithTag("S"),
|
STRIKE: replaceWithTag("S"),
|
||||||
SPAN: replaceStyles,
|
SPAN: replaceStyles,
|
||||||
FONT: node => {
|
FONT: node => {
|
||||||
let face = node.face,
|
const face = node.face;
|
||||||
size = node.size,
|
const size = node.size;
|
||||||
color = node.color,
|
let color = node.color;
|
||||||
newTag = createElement("SPAN"),
|
let newTag = createElement("SPAN");
|
||||||
css = newTag.style;
|
let css = newTag.style;
|
||||||
newTag.style.cssText = node.style.cssText;
|
newTag.style.cssText = node.style.cssText;
|
||||||
if (face) {
|
if (face) {
|
||||||
css.fontFamily = face;
|
css.fontFamily = face;
|
||||||
|
|
@ -592,8 +592,8 @@ const
|
||||||
css.fontSize = fontSizes[size];
|
css.fontSize = fontSizes[size];
|
||||||
}
|
}
|
||||||
if (color && /^#?([\dA-F]{3}){1,2}$/i.test(color)) {
|
if (color && /^#?([\dA-F]{3}){1,2}$/i.test(color)) {
|
||||||
if (color.charAt(0) !== '#') {
|
if (color.charAt(0) !== "#") {
|
||||||
color = '#' + color;
|
color = "#" + color;
|
||||||
}
|
}
|
||||||
css.color = color;
|
css.color = color;
|
||||||
}
|
}
|
||||||
|
|
@ -624,15 +624,15 @@ const
|
||||||
2. Convert inline tags into our preferred format.
|
2. Convert inline tags into our preferred format.
|
||||||
*/
|
*/
|
||||||
cleanTree = (node, preserveWS) => {
|
cleanTree = (node, preserveWS) => {
|
||||||
let children = node.childNodes,
|
const children = node.childNodes;
|
||||||
nonInlineParent, i = children.length, child, nodeName, childLength;
|
let nonInlineParent, i = children.length, child, nodeName, childLength;
|
||||||
// startsWithWS, endsWithWS, data, sibling;
|
// startsWithWS, endsWithWS, data, sibling;
|
||||||
|
|
||||||
nonInlineParent = node;
|
nonInlineParent = node;
|
||||||
while (isInline(nonInlineParent)) {
|
while (isInline(nonInlineParent)) {
|
||||||
nonInlineParent = nonInlineParent.parentNode;
|
nonInlineParent = nonInlineParent.parentNode;
|
||||||
}
|
}
|
||||||
// let walker = createTreeWalker(nonInlineParent, SHOW_ELEMENT_OR_TEXT);
|
// const walker = createTreeWalker(nonInlineParent, SHOW_ELEMENT_OR_TEXT);
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
child = children[i];
|
child = children[i];
|
||||||
|
|
@ -836,8 +836,8 @@ const
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rangeDoesEndAtBlockBoundary = (range, root) => {
|
rangeDoesEndAtBlockBoundary = (range, root) => {
|
||||||
const endContainer = range.endContainer,
|
const endContainer = range.endContainer;
|
||||||
endOffset = range.endOffset;
|
const endOffset = range.endOffset;
|
||||||
let currentNode;
|
let currentNode;
|
||||||
// If in a text node with content, and not at the end, we're not at the boundary
|
// If in a text node with content, and not at the end, we're not at the boundary
|
||||||
if (isTextNode(endContainer)) {
|
if (isTextNode(endContainer)) {
|
||||||
|
|
@ -860,9 +860,9 @@ const
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expandRangeToBlockBoundaries = (range, root) => {
|
expandRangeToBlockBoundaries = (range, root) => {
|
||||||
const start = getStartBlockOfRange(range, root),
|
const start = getStartBlockOfRange(range, root);
|
||||||
end = getEndBlockOfRange(range, root);
|
const end = getEndBlockOfRange(range, root);
|
||||||
|
// let parent;
|
||||||
if (start && end) {
|
if (start && end) {
|
||||||
range.setStart(start, 0);
|
range.setStart(start, 0);
|
||||||
range.setEnd(end, end.childNodes.length);
|
range.setEnd(end, end.childNodes.length);
|
||||||
|
|
@ -1509,13 +1509,8 @@ const
|
||||||
// If at very beginning of text area, allow backspace
|
// If at very beginning of text area, allow backspace
|
||||||
// to break lists/blockquote.
|
// to break lists/blockquote.
|
||||||
else if (current) {
|
else if (current) {
|
||||||
let parent = getClosest(current, root, 'UL,OL,BLOCKQUOTE');
|
if (decreaseLevel(self, range, current)) {
|
||||||
if (parent) {
|
return;
|
||||||
return ("BLOCKQUOTE" === parent.nodeName)
|
|
||||||
// Break blockquote
|
|
||||||
? self.decreaseQuoteLevel(range)
|
|
||||||
// Break list
|
|
||||||
: self.decreaseListLevel(range);
|
|
||||||
}
|
}
|
||||||
self.setRange(range);
|
self.setRange(range);
|
||||||
}
|
}
|
||||||
|
|
@ -1527,22 +1522,26 @@ const
|
||||||
setTimeout(() => afterDelete(self), 0);
|
setTimeout(() => afterDelete(self), 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// source/keyboard/Delete.ts
|
// source/keyboard/Delete.ts
|
||||||
Delete = (self, event, range) => {
|
Delete = (self, event, range) => {
|
||||||
const root = self._root;
|
const root = self._root;
|
||||||
let current, next, originalRange,
|
let current;
|
||||||
cursorContainer, cursorOffset, nodeAfterCursor;
|
let next;
|
||||||
|
let originalRange;
|
||||||
|
let cursorContainer;
|
||||||
|
let cursorOffset;
|
||||||
|
let nodeAfterCursor;
|
||||||
self._removeZWS();
|
self._removeZWS();
|
||||||
// Record undo checkpoint.
|
// Record undo checkpoint.
|
||||||
self.saveUndoState(range);
|
self.saveUndoState(range);
|
||||||
// If not collapsed, delete contents
|
|
||||||
if (!range.collapsed) {
|
if (!range.collapsed) {
|
||||||
|
// If not collapsed, delete contents
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
deleteContentsOfRange(range, root);
|
deleteContentsOfRange(range, root);
|
||||||
afterDelete(self, range);
|
afterDelete(self, range);
|
||||||
}
|
} else if (rangeDoesEndAtBlockBoundary(range, root)) {
|
||||||
// If at end of block, merge next into this block
|
// If at end of block, merge next into this block
|
||||||
else if (rangeDoesEndAtBlockBoundary(range, root)) {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (current = getStartBlockOfRange(range, root)) {
|
if (current = getStartBlockOfRange(range, root)) {
|
||||||
// In case inline data has somehow got between blocks.
|
// In case inline data has somehow got between blocks.
|
||||||
|
|
@ -1569,10 +1568,9 @@ const
|
||||||
self.setRange(range);
|
self.setRange(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
// Otherwise, leave to browser but check afterwards whether it has
|
// Otherwise, leave to browser but check afterwards whether it has
|
||||||
// left behind an empty inline tag.
|
// left behind an empty inline tag.
|
||||||
else {
|
|
||||||
// But first check if the cursor is just before an IMG tag. If so,
|
// But first check if the cursor is just before an IMG tag. If so,
|
||||||
// delete it ourselves, because the browser won't if it is not
|
// delete it ourselves, because the browser won't if it is not
|
||||||
// inline.
|
// inline.
|
||||||
|
|
@ -1594,6 +1592,107 @@ const
|
||||||
setTimeout(() => afterDelete(self), 0);
|
setTimeout(() => afterDelete(self), 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// source/keyboard/Tab.ts
|
||||||
|
Tab = (self, event, range) => {
|
||||||
|
self._removeZWS();
|
||||||
|
// If no selection and at start of block
|
||||||
|
if (range.collapsed && rangeDoesStartAtBlockBoundary(range, self._root)) {
|
||||||
|
getClosest(range.startContainer, self._root, "UL,OL,BLOCKQUOTE")
|
||||||
|
&& self.changeIndentationLevel('increase')
|
||||||
|
&& event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ShiftTab = (self, event, range) => {
|
||||||
|
self._removeZWS();
|
||||||
|
// If no selection and at start of block
|
||||||
|
if (range.collapsed && rangeDoesStartAtBlockBoundary(range, self._root)) {
|
||||||
|
// Break list
|
||||||
|
decreaseLevel(self, range, range.startContainer)
|
||||||
|
&& event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// source/keyboard/Space.ts
|
||||||
|
Space = (self, event, range) => {
|
||||||
|
/*
|
||||||
|
var _a;
|
||||||
|
let node;
|
||||||
|
const root = self._root;
|
||||||
|
self._recordUndoState(range);
|
||||||
|
self._getRangeAndRemoveBookmark(range);
|
||||||
|
if (!range.collapsed) {
|
||||||
|
deleteContentsOfRange(range, root);
|
||||||
|
self._ensureBottomLine();
|
||||||
|
self.setSelection(range);
|
||||||
|
self._updatePath(range, true);
|
||||||
|
} else if (rangeDoesEndAtBlockBoundary(range, root)) {
|
||||||
|
const block = getStartBlockOfRange(range, root);
|
||||||
|
if (block && block.nodeName !== "PRE") {
|
||||||
|
const text = (_a = block.textContent) == null ? void 0 : _a.trimEnd().replace(ZWS, "");
|
||||||
|
if (text === "*" || text === "1.") {
|
||||||
|
event.preventDefault();
|
||||||
|
const walker = new TreeIterator(block, SHOW_TEXT);
|
||||||
|
let textNode;
|
||||||
|
while (textNode = walker.nextNode()) {
|
||||||
|
textNode.data = cantFocusEmptyTextNodes ? ZWS : "";
|
||||||
|
}
|
||||||
|
if (text === "*") {
|
||||||
|
self.makeUnorderedList();
|
||||||
|
} else {
|
||||||
|
self.makeOrderedList();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = range.endContainer;
|
||||||
|
if (range.endOffset === getLength(node)) {
|
||||||
|
do {
|
||||||
|
if (node.nodeName === "A") {
|
||||||
|
range.setStartAfter(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!node.nextSibling && (node = node.parentNode) && node !== root);
|
||||||
|
}
|
||||||
|
if (self._config.addLinks) {
|
||||||
|
const linkRange = range.cloneRange();
|
||||||
|
moveRangeBoundariesDownTree(linkRange);
|
||||||
|
const textNode = linkRange.startContainer;
|
||||||
|
const offset = linkRange.startOffset;
|
||||||
|
setTimeout(() => {
|
||||||
|
linkifyText(self, textNode, offset);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
self.setSelection(range);
|
||||||
|
*/
|
||||||
|
const root = self._root;
|
||||||
|
self._recordUndoState(range);
|
||||||
|
self._config.addLinks && addLinks(range.startContainer, root);
|
||||||
|
self._getRangeAndRemoveBookmark(range);
|
||||||
|
/*
|
||||||
|
// If the cursor is at the end of a link (<a>foo|</a>) then move it
|
||||||
|
// outside of the link (<a>foo</a>|) so that the space is not part of
|
||||||
|
// the link text.
|
||||||
|
// SnappyMail: disabled as it fails in Firefox
|
||||||
|
let node = range.endContainer;
|
||||||
|
if (range.collapsed && range.endOffset === getLength(node)) {
|
||||||
|
do {
|
||||||
|
if (node.nodeName === "A") {
|
||||||
|
range.setStartAfter(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!node.nextSibling && (node = node.parentNode) && node !== root);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Delete the selection if not collapsed
|
||||||
|
if (!range.collapsed) {
|
||||||
|
deleteContentsOfRange(range, root);
|
||||||
|
self._ensureBottomLine();
|
||||||
|
}
|
||||||
|
self.setRange(range);
|
||||||
|
},
|
||||||
|
|
||||||
// source/keyboard/KeyHandlers.ts
|
// source/keyboard/KeyHandlers.ts
|
||||||
onKey = function(event) {
|
onKey = function(event) {
|
||||||
if (event.defaultPrevented) {
|
if (event.defaultPrevented) {
|
||||||
|
|
@ -1650,67 +1749,9 @@ const
|
||||||
keyHandlers = {
|
keyHandlers = {
|
||||||
// "backspace": Backspace,
|
// "backspace": Backspace,
|
||||||
// "delete": Delete,
|
// "delete": Delete,
|
||||||
tab: (self, event, range) => {
|
tab: Tab,
|
||||||
let root = self._root;
|
'shift-tab': ShiftTab,
|
||||||
let node, parent;
|
space: Space,
|
||||||
self._removeZWS();
|
|
||||||
// If no selection and at start of block
|
|
||||||
if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) {
|
|
||||||
node = getStartBlockOfRange(range, root);
|
|
||||||
// Iterate through the block's parents
|
|
||||||
while (parent = node.parentNode) {
|
|
||||||
// If we find a UL or OL (so are in a list, node must be an LI)
|
|
||||||
if (parent.nodeName === "UL" || parent.nodeName === "OL") {
|
|
||||||
// Then increase the list level
|
|
||||||
event.preventDefault();
|
|
||||||
self.increaseListLevel(range);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
node = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'shift-tab': (self, event, range) => {
|
|
||||||
let root = self._root;
|
|
||||||
let node;
|
|
||||||
self._removeZWS();
|
|
||||||
// If no selection and at start of block
|
|
||||||
if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) {
|
|
||||||
// Break list
|
|
||||||
node = range.startContainer;
|
|
||||||
if (getClosest(node, root, 'UL,OL')) {
|
|
||||||
event.preventDefault();
|
|
||||||
self.decreaseListLevel(range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
space: (self, _, range) => {
|
|
||||||
let root = self._root;
|
|
||||||
self._recordUndoState(range);
|
|
||||||
self._config.addLinks && addLinks(range.startContainer, root);
|
|
||||||
self._getRangeAndRemoveBookmark(range);
|
|
||||||
/*
|
|
||||||
// If the cursor is at the end of a link (<a>foo|</a>) then move it
|
|
||||||
// outside of the link (<a>foo</a>|) so that the space is not part of
|
|
||||||
// the link text.
|
|
||||||
// SnappyMail: disabled as it fails in Firefox
|
|
||||||
let node = range.endContainer;
|
|
||||||
if (range.collapsed && range.endOffset === getLength(node)) {
|
|
||||||
do {
|
|
||||||
if (node.nodeName === "A") {
|
|
||||||
range.setStartAfter(node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (!node.nextSibling && (node = node.parentNode) && node !== root);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// Delete the selection if not collapsed
|
|
||||||
if (!range.collapsed) {
|
|
||||||
deleteContentsOfRange(range, root);
|
|
||||||
self._ensureBottomLine();
|
|
||||||
}
|
|
||||||
self.setRange(range);
|
|
||||||
},
|
|
||||||
arrowleft: self => self._removeZWS(),
|
arrowleft: self => self._removeZWS(),
|
||||||
arrowright: self => self._removeZWS()
|
arrowright: self => self._removeZWS()
|
||||||
},
|
},
|
||||||
|
|
@ -1973,6 +2014,9 @@ const
|
||||||
return frag;
|
return frag;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
decreaseLevel = (self, range, node) =>
|
||||||
|
getClosest(node, self._root, 'UL,OL,BLOCKQUOTE') && self.changeIndentationLevel('decrease'),
|
||||||
|
|
||||||
linkRegExp = /\b(?:((https?:\/\/)?(?:www\d{0,3}\.|[a-z0-9][a-z0-9.-]*\.[a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:[^\s?&`!()[\]{};:'".,<>«»“”‘’]|\([^\s()<>]+\)))|([\w\-.%+]+@(?:[\w-]+\.)+[a-z]{2,}\b(?:\?[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+(?:&[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+)*)?))/i,
|
linkRegExp = /\b(?:((https?:\/\/)?(?:www\d{0,3}\.|[a-z0-9][a-z0-9.-]*\.[a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:[^\s?&`!()[\]{};:'".,<>«»“”‘’]|\([^\s()<>]+\)))|([\w\-.%+]+@(?:[\w-]+\.)+[a-z]{2,}\b(?:\?[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+(?:&[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+)*)?))/i,
|
||||||
|
|
||||||
addLinks = (frag, root) => {
|
addLinks = (frag, root) => {
|
||||||
|
|
@ -2397,7 +2441,7 @@ class Squire
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveRangeToBookmark(range) {
|
_saveRangeToBookmark(range) {
|
||||||
let [startNode, endNode] = createBookmarkNodes(this),
|
let [startNode, endNode] = createBookmarkNodes(),
|
||||||
temp;
|
temp;
|
||||||
|
|
||||||
insertNodeInRange(range, startNode);
|
insertNodeInRange(range, startNode);
|
||||||
|
|
@ -3213,7 +3257,7 @@ class Squire
|
||||||
// TODO: why was _docWasChanged() not triggered?
|
// TODO: why was _docWasChanged() not triggered?
|
||||||
this.editStack.inUndoState && this._docWasChanged();
|
this.editStack.inUndoState && this._docWasChanged();
|
||||||
this._recordUndoState(range);
|
this._recordUndoState(range);
|
||||||
// self._config.addLinks && addLinks(range.startContainer, root);
|
// this._config.addLinks && addLinks(range.startContainer, root);
|
||||||
this._removeZWS();
|
this._removeZWS();
|
||||||
this._getRangeAndRemoveBookmark(range);
|
this._getRangeAndRemoveBookmark(range);
|
||||||
|
|
||||||
|
|
@ -3226,7 +3270,7 @@ class Squire
|
||||||
// const offset2 = range.startOffset;
|
// const offset2 = range.startOffset;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// linkifyText(this, textNode, offset2);
|
// linkifyText(this, textNode, offset2);
|
||||||
addLinks(range.startContainer, self._root);
|
addLinks(range.startContainer, this._root);
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
block = getStartBlockOfRange(range, root);
|
block = getStartBlockOfRange(range, root);
|
||||||
|
|
@ -3285,12 +3329,12 @@ class Squire
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
block = getClosest(block, root, "LI") || block;
|
block = getClosest(block, root, "LI") || block;
|
||||||
if (isEmptyBlock(block) && (parent = getClosest(block, root, 'UL,OL,BLOCKQUOTE'))) {
|
if (isEmptyBlock(block) && (parent = getClosest(block, root, "UL,OL,BLOCKQUOTE"))) {
|
||||||
return "BLOCKQUOTE" === parent.nodeName
|
return "BLOCKQUOTE" === parent.nodeName
|
||||||
// Break blockquote
|
// Break blockquote
|
||||||
? self.modifyBlocks((/* frag */) => self.createDefaultBlock(createBookmarkNodes(self)), range)
|
? this.modifyBlocks((/* frag */) => this.createDefaultBlock(createBookmarkNodes()), range)
|
||||||
// Break list
|
// Break list
|
||||||
: self.decreaseListLevel(range);
|
: this.decreaseListLevel(range);
|
||||||
}
|
}
|
||||||
node = range.startContainer;
|
node = range.startContainer;
|
||||||
const offset = range.startOffset;
|
const offset = range.startOffset;
|
||||||
|
|
@ -3645,7 +3689,7 @@ class Squire
|
||||||
let parent = this.getSelectionClosest('UL,OL,BLOCKQUOTE');
|
let parent = this.getSelectionClosest('UL,OL,BLOCKQUOTE');
|
||||||
if (parent || "increase" === direction) {
|
if (parent || "increase" === direction) {
|
||||||
let method = (!parent || "BLOCKQUOTE" === parent.nodeName) ? "Quote" : "List";
|
let method = (!parent || "BLOCKQUOTE" === parent.nodeName) ? "Quote" : "List";
|
||||||
this[direction + method + "Level"]();
|
return this[direction + method + "Level"]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue