mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-10-24 22:25:09 +03:00
Squire v2.4.9
This commit is contained in:
parent
8667af700d
commit
020927cc56
1 changed files with 584 additions and 80 deletions
664
vendors/squire2/dist/squire-raw.js
vendored
664
vendors/squire2/dist/squire-raw.js
vendored
|
|
@ -45,6 +45,7 @@
|
||||||
// source/Constants.ts
|
// source/Constants.ts
|
||||||
var ELEMENT_NODE = 1;
|
var ELEMENT_NODE = 1;
|
||||||
var TEXT_NODE = 3;
|
var TEXT_NODE = 3;
|
||||||
|
var COMMENT_NODE = 8;
|
||||||
var DOCUMENT_FRAGMENT_NODE = 11;
|
var DOCUMENT_FRAGMENT_NODE = 11;
|
||||||
var ZWS = "\u200B";
|
var ZWS = "\u200B";
|
||||||
var ua = navigator.userAgent;
|
var ua = navigator.userAgent;
|
||||||
|
|
@ -55,7 +56,7 @@
|
||||||
var isWebKit = /WebKit\//.test(ua);
|
var isWebKit = /WebKit\//.test(ua);
|
||||||
var ctrlKey = isMac || isIOS ? "Meta-" : "Ctrl-";
|
var ctrlKey = isMac || isIOS ? "Meta-" : "Ctrl-";
|
||||||
var cantFocusEmptyTextNodes = isWebKit;
|
var cantFocusEmptyTextNodes = isWebKit;
|
||||||
var notWS = /[^ \t\r\n]/;
|
var notWS = /[^ \t\r\n\u200B]/;
|
||||||
|
|
||||||
// source/node/Category.ts
|
// source/node/Category.ts
|
||||||
var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|EL|FN)|EM|FONT|HR|I(?:MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:AMP|MALL|PAN|TR(?:IKE|ONG)|U[BP])?|TIME|U|VAR|WBR)$/;
|
var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|EL|FN)|EM|FONT|HR|I(?:MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:AMP|MALL|PAN|TR(?:IKE|ONG)|U[BP])?|TIME|U|VAR|WBR)$/;
|
||||||
|
|
@ -71,6 +72,7 @@
|
||||||
var isLeaf = (node) => leafNodeNames.has(node.nodeName);
|
var isLeaf = (node) => leafNodeNames.has(node.nodeName);
|
||||||
var getNodeCategory = (node) => {
|
var getNodeCategory = (node) => {
|
||||||
switch (node.nodeType) {
|
switch (node.nodeType) {
|
||||||
|
case COMMENT_NODE:
|
||||||
case TEXT_NODE:
|
case TEXT_NODE:
|
||||||
return INLINE;
|
return INLINE;
|
||||||
case ELEMENT_NODE:
|
case ELEMENT_NODE:
|
||||||
|
|
@ -252,11 +254,12 @@
|
||||||
return nodeStartAfterStart && nodeEndBeforeEnd;
|
return nodeStartAfterStart && nodeEndBeforeEnd;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var canDescendInto = (node) => node instanceof Text || node.nodeType === ELEMENT_NODE && !isLeaf(node);
|
||||||
var moveRangeBoundariesDownTree = (range) => {
|
var moveRangeBoundariesDownTree = (range) => {
|
||||||
let { startContainer, startOffset, endContainer, endOffset } = range;
|
let { startContainer, startOffset, endContainer, endOffset } = range;
|
||||||
while (!(startContainer instanceof Text)) {
|
while (!(startContainer instanceof Text)) {
|
||||||
let child = startContainer.childNodes[startOffset];
|
let child = startContainer.childNodes[startOffset];
|
||||||
if (!child || isLeaf(child)) {
|
if (!child || !canDescendInto(child)) {
|
||||||
if (startOffset) {
|
if (startOffset) {
|
||||||
child = startContainer.childNodes[startOffset - 1];
|
child = startContainer.childNodes[startOffset - 1];
|
||||||
if (child instanceof Text) {
|
if (child instanceof Text) {
|
||||||
|
|
@ -278,7 +281,7 @@
|
||||||
if (endOffset) {
|
if (endOffset) {
|
||||||
while (!(endContainer instanceof Text)) {
|
while (!(endContainer instanceof Text)) {
|
||||||
const child = endContainer.childNodes[endOffset - 1];
|
const child = endContainer.childNodes[endOffset - 1];
|
||||||
if (!child || isLeaf(child)) {
|
if (!child || !canDescendInto(child)) {
|
||||||
if (child && child.nodeName === "BR" && !isLineBreak(child)) {
|
if (child && child.nodeName === "BR" && !isLineBreak(child)) {
|
||||||
--endOffset;
|
--endOffset;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -291,7 +294,7 @@
|
||||||
} else {
|
} else {
|
||||||
while (!(endContainer instanceof Text)) {
|
while (!(endContainer instanceof Text)) {
|
||||||
const child = endContainer.firstChild;
|
const child = endContainer.firstChild;
|
||||||
if (!child || isLeaf(child)) {
|
if (!child || !canDescendInto(child)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
endContainer = child;
|
endContainer = child;
|
||||||
|
|
@ -368,7 +371,7 @@
|
||||||
replace(classNames, family) {
|
replace(classNames, family) {
|
||||||
return createElement("SPAN", {
|
return createElement("SPAN", {
|
||||||
class: classNames.fontFamily,
|
class: classNames.fontFamily,
|
||||||
style: "font-family:" + family
|
style: { fontFamily: family }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -377,7 +380,7 @@
|
||||||
replace(classNames, size) {
|
replace(classNames, size) {
|
||||||
return createElement("SPAN", {
|
return createElement("SPAN", {
|
||||||
class: classNames.fontSize,
|
class: classNames.fontSize,
|
||||||
style: "font-size:" + size
|
style: { fontSize: size }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -557,6 +560,9 @@
|
||||||
};
|
};
|
||||||
var fixContainer = (container) => {
|
var fixContainer = (container) => {
|
||||||
let wrapper = null;
|
let wrapper = null;
|
||||||
|
if (/^(?:TABLE|TBODY|TR|TH|TD|P)/.test(container.nodeName)) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
[...container.childNodes].forEach((child) => {
|
[...container.childNodes].forEach((child) => {
|
||||||
if (isInline(child)) {
|
if (isInline(child)) {
|
||||||
wrapper || (wrapper = createElement("DIV"));
|
wrapper || (wrapper = createElement("DIV"));
|
||||||
|
|
@ -566,10 +572,13 @@
|
||||||
wrapper = null;
|
wrapper = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
wrapper && container.append(wrapper);
|
wrapper && container.append(fixCursor(wrapper));
|
||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
var split = (node, offset, stopNode, root) => {
|
var split = (node, offset, stopNode, root) => {
|
||||||
|
if (!stopNode.contains(node)) {
|
||||||
|
throw new Error("split: stopNode does not contain node");
|
||||||
|
}
|
||||||
if (node instanceof Text && node !== stopNode) {
|
if (node instanceof Text && node !== stopNode) {
|
||||||
if (typeof offset !== "number") {
|
if (typeof offset !== "number") {
|
||||||
throw new Error("Offset must be a number to split text node!");
|
throw new Error("Offset must be a number to split text node!");
|
||||||
|
|
@ -1020,7 +1029,7 @@
|
||||||
}
|
}
|
||||||
moveRangeBoundariesDownTree(range);
|
moveRangeBoundariesDownTree(range);
|
||||||
range.collapse(false);
|
range.collapse(false);
|
||||||
const stopPoint = getClosest(range.endContainer, root, "BLOCKQUOTE") || root;
|
let stopPoint = getClosest(range.endContainer, root, "BLOCKQUOTE") || root;
|
||||||
let block = getStartBlockOfRange(range, root);
|
let block = getStartBlockOfRange(range, root);
|
||||||
let blockContentsAfterSplit = null;
|
let blockContentsAfterSplit = null;
|
||||||
const firstBlockInFrag = getNextBlock(frag, frag);
|
const firstBlockInFrag = getNextBlock(frag, frag);
|
||||||
|
|
@ -1054,11 +1063,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mergeWithBlock(container, firstBlockInFrag, range, root);
|
mergeWithBlock(container, firstBlockInFrag, range, root);
|
||||||
offset = Array.from(container.parentNode.childNodes).indexOf(
|
if (container === root) {
|
||||||
container
|
range.setEnd(root, getLength(root));
|
||||||
) + 1;
|
} else {
|
||||||
container = container.parentNode;
|
offset = Array.from(container.parentNode.childNodes).indexOf(
|
||||||
range.setEnd(container, offset);
|
container
|
||||||
|
) + 1;
|
||||||
|
container = container.parentNode;
|
||||||
|
range.setEnd(container, offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (getLength(frag)) {
|
if (getLength(frag)) {
|
||||||
if (replaceBlock && block) {
|
if (replaceBlock && block) {
|
||||||
|
|
@ -1066,6 +1079,13 @@
|
||||||
range.collapse(false);
|
range.collapse(false);
|
||||||
detach(block);
|
detach(block);
|
||||||
}
|
}
|
||||||
|
if (!stopPoint.contains(range.endContainer)) {
|
||||||
|
if (!root.contains(range.endContainer)) {
|
||||||
|
range.setEnd(root, getLength(root));
|
||||||
|
range.collapse(false);
|
||||||
|
}
|
||||||
|
stopPoint = getClosest(range.endContainer, root, "BLOCKQUOTE") || root;
|
||||||
|
}
|
||||||
moveRangeBoundariesUpTree(range, stopPoint, stopPoint, root);
|
moveRangeBoundariesUpTree(range, stopPoint, stopPoint, root);
|
||||||
let nodeAfterSplit = split(
|
let nodeAfterSplit = split(
|
||||||
range.endContainer,
|
range.endContainer,
|
||||||
|
|
@ -1149,29 +1169,25 @@
|
||||||
|
|
||||||
// source/Clipboard.ts
|
// source/Clipboard.ts
|
||||||
var indexOf = Array.prototype.indexOf;
|
var indexOf = Array.prototype.indexOf;
|
||||||
var extractRangeToClipboard = (event, range, root, removeRangeFromDocument, toCleanHTML, toPlainText, plainTextOnly) => {
|
var extractRange = (range, root, removeRangeFromDocument, toCleanHTML, toPlainText) => {
|
||||||
const clipboardData = event.clipboardData;
|
|
||||||
if (!clipboardData) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let text = toPlainText ? "" : getTextContentsOfRange(range);
|
let text = toPlainText ? "" : getTextContentsOfRange(range);
|
||||||
const startBlock = getStartBlockOfRange(range, root);
|
const startBlock = getStartBlockOfRange(range, root);
|
||||||
const endBlock = getEndBlockOfRange(range, root);
|
const endBlock = getEndBlockOfRange(range, root);
|
||||||
|
let parent = range.commonAncestorContainer;
|
||||||
let copyRoot = root;
|
let copyRoot = root;
|
||||||
if (startBlock === endBlock && startBlock?.contains(range.commonAncestorContainer)) {
|
if (startBlock === endBlock && startBlock?.contains(parent)) {
|
||||||
copyRoot = startBlock;
|
copyRoot = startBlock;
|
||||||
}
|
}
|
||||||
let contents;
|
let contents;
|
||||||
if (removeRangeFromDocument) {
|
if (removeRangeFromDocument) {
|
||||||
contents = deleteContentsOfRange(range, root);
|
contents = deleteContentsOfRange(range, root);
|
||||||
|
if (!parent.isConnected) {
|
||||||
|
parent = range.commonAncestorContainer;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
range = range.cloneRange();
|
|
||||||
moveRangeBoundariesDownTree(range);
|
|
||||||
moveRangeBoundariesUpTree(range, copyRoot, copyRoot, root);
|
|
||||||
contents = range.cloneContents();
|
contents = range.cloneContents();
|
||||||
}
|
}
|
||||||
let parent = range.commonAncestorContainer;
|
while (parent && !(parent instanceof Element)) {
|
||||||
if (parent instanceof Text) {
|
|
||||||
parent = parent.parentNode;
|
parent = parent.parentNode;
|
||||||
}
|
}
|
||||||
while (parent && parent !== copyRoot) {
|
while (parent && parent !== copyRoot) {
|
||||||
|
|
@ -1183,7 +1199,7 @@
|
||||||
let html;
|
let html;
|
||||||
if (contents.childNodes.length === 1 && contents.childNodes[0] instanceof Text) {
|
if (contents.childNodes.length === 1 && contents.childNodes[0] instanceof Text) {
|
||||||
text = contents.childNodes[0].data.replace(/ /g, " ");
|
text = contents.childNodes[0].data.replace(/ /g, " ");
|
||||||
plainTextOnly = true;
|
html = void 0;
|
||||||
} else {
|
} else {
|
||||||
const node = createElement("DIV");
|
const node = createElement("DIV");
|
||||||
node.append(contents);
|
node.append(contents);
|
||||||
|
|
@ -1195,15 +1211,34 @@
|
||||||
if (toPlainText && html !== void 0) {
|
if (toPlainText && html !== void 0) {
|
||||||
text = toPlainText(html);
|
text = toPlainText(html);
|
||||||
}
|
}
|
||||||
|
if (text === html) {
|
||||||
|
html = void 0;
|
||||||
|
}
|
||||||
if (isWin) {
|
if (isWin) {
|
||||||
text = text.replace(/\r?\n/g, "\r\n");
|
text = text.replace(/\r?\n/g, "\r\n");
|
||||||
}
|
}
|
||||||
if (!plainTextOnly && html && text !== html) {
|
if (html) {
|
||||||
html = "<!-- squire -->" + html;
|
html = "<!-- squire -->" + html;
|
||||||
|
}
|
||||||
|
return [text, html];
|
||||||
|
};
|
||||||
|
var extractRangeToClipboard = (event, range, root, removeRangeFromDocument, toCleanHTML, toPlainText, plainTextOnly) => {
|
||||||
|
const clipboardData = event.clipboardData;
|
||||||
|
if (!clipboardData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let [text, html] = extractRange(
|
||||||
|
range,
|
||||||
|
root,
|
||||||
|
removeRangeFromDocument,
|
||||||
|
toCleanHTML,
|
||||||
|
toPlainText
|
||||||
|
);
|
||||||
|
event.preventDefault();
|
||||||
|
if (!plainTextOnly && html) {
|
||||||
clipboardData.setData("text/html", html);
|
clipboardData.setData("text/html", html);
|
||||||
}
|
}
|
||||||
clipboardData.setData("text/plain", text);
|
clipboardData.setData("text/plain", text);
|
||||||
event.preventDefault();
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
var _onCut = function(event) {
|
var _onCut = function(event) {
|
||||||
|
|
@ -1251,7 +1286,11 @@
|
||||||
var _onPaste = function(event) {
|
var _onPaste = function(event) {
|
||||||
const clipboardData = event.clipboardData;
|
const clipboardData = event.clipboardData;
|
||||||
const items = clipboardData.items;
|
const items = clipboardData.items;
|
||||||
const choosePlain = this._isShiftDown;
|
const choosePlain = this._isShiftDown || !!getNearest(
|
||||||
|
this.getSelection().commonAncestorContainer,
|
||||||
|
this._root,
|
||||||
|
"PRE"
|
||||||
|
);
|
||||||
let hasRTF = false;
|
let hasRTF = false;
|
||||||
let hasImage = false;
|
let hasImage = false;
|
||||||
let plainItem = null;
|
let plainItem = null;
|
||||||
|
|
@ -1270,7 +1309,7 @@
|
||||||
hasImage = true;
|
hasImage = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasImage && !(hasRTF && htmlItem)) {
|
if (hasImage && !(hasRTF && htmlItem) && !plainItem) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.fireEvent("pasteImage", {
|
this.fireEvent("pasteImage", {
|
||||||
clipboardData
|
clipboardData
|
||||||
|
|
@ -1284,42 +1323,122 @@
|
||||||
});
|
});
|
||||||
} else if (plainItem) {
|
} else if (plainItem) {
|
||||||
plainItem.getAsString((text) => {
|
plainItem.getAsString((text) => {
|
||||||
let isLink = false;
|
|
||||||
const range = this.getSelection();
|
const range = this.getSelection();
|
||||||
if (!range.collapsed && notWS.test(range.toString())) {
|
if (!range.collapsed && notWS.test(range.toString())) {
|
||||||
const match = this.linkRegExp.exec(text);
|
const match = this.linkRegExp.exec(text);
|
||||||
isLink = !!match && match[0].length === text.length;
|
const isLink = !!match && match[0].length === text.length;
|
||||||
}
|
if (isLink) {
|
||||||
if (isLink) {
|
const href = match[1] ? /^(?:ht|f)tps?:/i.test(match[1]) ? match[1] : "http://" + match[1] : "mailto:" + match[0];
|
||||||
this.makeLink(text);
|
this.makeLink(href);
|
||||||
} else {
|
return;
|
||||||
this.insertPlainText(text, true);
|
}
|
||||||
}
|
}
|
||||||
|
this.insertPlainText(text, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var _onDragStart = function() {
|
||||||
|
const range = this.getSelection();
|
||||||
|
if (range && !range.collapsed && this._root.contains(range.commonAncestorContainer)) {
|
||||||
|
this._dragRange = range.cloneRange();
|
||||||
|
} else {
|
||||||
|
this._dragRange = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var _onDragEnd = function() {
|
||||||
|
this._dragRange = null;
|
||||||
|
};
|
||||||
|
var getCaretRangeFromPoint = (x, y, root) => {
|
||||||
|
let range = null;
|
||||||
|
const doc = document;
|
||||||
|
if (doc.caretPositionFromPoint) {
|
||||||
|
const pos = doc.caretPositionFromPoint(x, y);
|
||||||
|
if (pos) {
|
||||||
|
range = document.createRange();
|
||||||
|
range.setStart(pos.offsetNode, pos.offset);
|
||||||
|
range.collapse(true);
|
||||||
|
}
|
||||||
|
} else if (doc.caretRangeFromPoint) {
|
||||||
|
range = doc.caretRangeFromPoint(x, y);
|
||||||
|
}
|
||||||
|
if (range && !root.contains(range.commonAncestorContainer)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
var pointIsWithinRange = (point, source) => {
|
||||||
|
return point.compareBoundaryPoints(Range.START_TO_START, source) >= 0 && point.compareBoundaryPoints(Range.END_TO_END, source) <= 0;
|
||||||
|
};
|
||||||
var _onDrop = function(event) {
|
var _onDrop = function(event) {
|
||||||
if (!event.dataTransfer) {
|
const dataTransfer = event.dataTransfer;
|
||||||
|
if (!dataTransfer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const types = event.dataTransfer.types;
|
const types = dataTransfer.types;
|
||||||
let l = types.length;
|
|
||||||
let hasPlain = false;
|
let hasPlain = false;
|
||||||
let hasHTML = false;
|
let hasHTML = false;
|
||||||
while (l--) {
|
for (let i = 0, l = types.length; i < l; i += 1) {
|
||||||
switch (types[l]) {
|
switch (types[i]) {
|
||||||
case "text/plain":
|
case "text/plain":
|
||||||
hasPlain = true;
|
hasPlain = true;
|
||||||
break;
|
break;
|
||||||
case "text/html":
|
case "text/html":
|
||||||
hasHTML = true;
|
hasHTML = true;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasHTML || hasPlain && this.saveUndoState) {
|
if (!hasPlain && !hasHTML) {
|
||||||
this.saveUndoState();
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
const root = this._root;
|
||||||
|
let dropRange = getCaretRangeFromPoint(event.clientX, event.clientY, root);
|
||||||
|
const dragRange = this._dragRange;
|
||||||
|
this._dragRange = null;
|
||||||
|
if (!dropRange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let text, html;
|
||||||
|
if (dragRange) {
|
||||||
|
if (pointIsWithinRange(dropRange, dragRange)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._recordUndoState(dragRange, this._isInUndoState);
|
||||||
|
const bookmark = document.createComment("");
|
||||||
|
dropRange.insertNode(bookmark);
|
||||||
|
this._getRangeAndRemoveBookmark(dragRange);
|
||||||
|
[text, html] = extractRange(
|
||||||
|
dragRange,
|
||||||
|
root,
|
||||||
|
dataTransfer.dropEffect !== "copy",
|
||||||
|
this._config.willCutCopy,
|
||||||
|
this._config.toPlainText
|
||||||
|
);
|
||||||
|
bookmark.replaceWith(
|
||||||
|
createElement("INPUT", {
|
||||||
|
id: this.startSelectionId,
|
||||||
|
type: "hidden"
|
||||||
|
}),
|
||||||
|
createElement("INPUT", {
|
||||||
|
id: this.endSelectionId,
|
||||||
|
type: "hidden"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this._getRangeAndRemoveBookmark(dropRange);
|
||||||
|
resetNodeCategoryCache();
|
||||||
|
} else {
|
||||||
|
this.saveUndoState(dropRange);
|
||||||
|
if (hasHTML) {
|
||||||
|
html = dataTransfer.getData("text/html");
|
||||||
|
} else {
|
||||||
|
text = dataTransfer.getData("text/plain");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setSelection(dropRange);
|
||||||
|
if (html !== void 0) {
|
||||||
|
this.insertHTML(html, true);
|
||||||
|
} else if (text !== void 0) {
|
||||||
|
this.insertPlainText(text, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1639,8 +1758,9 @@
|
||||||
}
|
}
|
||||||
key = modifiers + key;
|
key = modifiers + key;
|
||||||
const range = this.getSelection();
|
const range = this.getSelection();
|
||||||
if (this._keyHandlers[key]) {
|
const handler = this._keyHandlers[key];
|
||||||
this._keyHandlers[key](this, event, range);
|
if (handler) {
|
||||||
|
handler(this, event, range);
|
||||||
} else if (!range.collapsed && !event.ctrlKey && !event.metaKey && key.length === 1) {
|
} else if (!range.collapsed && !event.ctrlKey && !event.metaKey && key.length === 1) {
|
||||||
this.saveUndoState(range);
|
this.saveUndoState(range);
|
||||||
deleteContentsOfRange(range, this._root);
|
deleteContentsOfRange(range, this._root);
|
||||||
|
|
@ -1667,7 +1787,7 @@
|
||||||
do {
|
do {
|
||||||
if (node.nodeName === "CODE") {
|
if (node.nodeName === "CODE") {
|
||||||
let next = node.nextSibling;
|
let next = node.nextSibling;
|
||||||
if (!(next instanceof Text)) {
|
if (!(next instanceof Text) || next.length === 0) {
|
||||||
const textNode = document.createTextNode("\xA0");
|
const textNode = document.createTextNode("\xA0");
|
||||||
node.parentNode.insertBefore(textNode, next);
|
node.parentNode.insertBefore(textNode, next);
|
||||||
next = textNode;
|
next = textNode;
|
||||||
|
|
@ -1759,6 +1879,331 @@
|
||||||
self.redo();
|
self.redo();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// source/ImageResize.ts
|
||||||
|
var RESIZE_HANDLE_SIZE = 8;
|
||||||
|
var MIN_IMAGE_SIZE = 40;
|
||||||
|
var MAX_IMAGE_SIZE = 1200;
|
||||||
|
var handlePositions = [
|
||||||
|
{ pos: "nw", cursor: "nwse-resize" },
|
||||||
|
{ pos: "n", cursor: "ns-resize" },
|
||||||
|
{ pos: "ne", cursor: "nesw-resize" },
|
||||||
|
{ pos: "e", cursor: "ew-resize" },
|
||||||
|
{ pos: "se", cursor: "nwse-resize" },
|
||||||
|
{ pos: "s", cursor: "ns-resize" },
|
||||||
|
{ pos: "sw", cursor: "nesw-resize" },
|
||||||
|
{ pos: "w", cursor: "ew-resize" }
|
||||||
|
];
|
||||||
|
var keyHandlers2 = {
|
||||||
|
ArrowUp: (editor, range, root) => {
|
||||||
|
const block = getStartBlockOfRange(range, root);
|
||||||
|
if (block) {
|
||||||
|
const prev = getPreviousBlock(block, root);
|
||||||
|
if (prev) {
|
||||||
|
range.selectNodeContents(prev);
|
||||||
|
range.collapse(false);
|
||||||
|
editor.setSelection(range).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ArrowDown: (editor, range, root) => {
|
||||||
|
const block = getStartBlockOfRange(range, root);
|
||||||
|
if (block) {
|
||||||
|
const next = getNextBlock(block, root);
|
||||||
|
if (next) {
|
||||||
|
range.selectNodeContents(next);
|
||||||
|
range.collapse(true);
|
||||||
|
editor.setSelection(range).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Delete: (editor, range) => {
|
||||||
|
editor.replaceWithBlankLine(range);
|
||||||
|
},
|
||||||
|
Backspace: (editor, range) => {
|
||||||
|
editor.replaceWithBlankLine(range);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var ImageResizer = class {
|
||||||
|
constructor(root, editor) {
|
||||||
|
this._currentImage = null;
|
||||||
|
this._resizeContainer = null;
|
||||||
|
this._handles = null;
|
||||||
|
this._currentHandle = null;
|
||||||
|
this._startX = 0;
|
||||||
|
this._startY = 0;
|
||||||
|
this._startWidth = 0;
|
||||||
|
this._startHeight = 0;
|
||||||
|
this._maxWidth = MAX_IMAGE_SIZE;
|
||||||
|
this._originalRatio = 1;
|
||||||
|
this._editor = editor;
|
||||||
|
this._root = root;
|
||||||
|
document.addEventListener("click", this);
|
||||||
|
editor.addEventListener("drop", this);
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
this._deselectImage();
|
||||||
|
this._editor.removeEventListener("drop", this);
|
||||||
|
document.removeEventListener("click", this);
|
||||||
|
}
|
||||||
|
// EventListener interface implementation
|
||||||
|
handleEvent(event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case "click":
|
||||||
|
this._onClick(event);
|
||||||
|
break;
|
||||||
|
case "pointerdown":
|
||||||
|
this._onPointerDown(event);
|
||||||
|
break;
|
||||||
|
case "pointermove":
|
||||||
|
this._onPointerMove(event);
|
||||||
|
break;
|
||||||
|
case "pointercancel":
|
||||||
|
case "pointerup":
|
||||||
|
this._onPointerUp(event);
|
||||||
|
break;
|
||||||
|
case "keydown":
|
||||||
|
this._onKeyDown(event);
|
||||||
|
break;
|
||||||
|
case "drop":
|
||||||
|
this._deselectImage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ---
|
||||||
|
_onClick(event) {
|
||||||
|
const target = event.target;
|
||||||
|
if (target.nodeName === "IMG" && this._root.contains(target)) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this._selectImage(target);
|
||||||
|
} else if (this._currentImage && this._handles && !this._handles.some((handle) => handle.element === target)) {
|
||||||
|
this._deselectImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_deselectImage() {
|
||||||
|
if (!this._currentImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.removeEventListener("keydown", this, true);
|
||||||
|
if (this._currentHandle) {
|
||||||
|
this._onPointerUp({
|
||||||
|
preventDefault() {
|
||||||
|
},
|
||||||
|
target: this._currentHandle.element
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this._handles) {
|
||||||
|
this._handles.forEach(
|
||||||
|
({ element }) => element.removeEventListener("pointerdown", this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this._resizeContainer) {
|
||||||
|
this._resizeContainer.remove();
|
||||||
|
}
|
||||||
|
this._currentImage.removeAttribute("tabindex");
|
||||||
|
this._handles = null;
|
||||||
|
this._resizeContainer = null;
|
||||||
|
this._currentImage = null;
|
||||||
|
}
|
||||||
|
_selectImage(image) {
|
||||||
|
if (this._currentImage === image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._deselectImage();
|
||||||
|
this._root.blur();
|
||||||
|
const handles = handlePositions.map(({ pos, cursor }) => {
|
||||||
|
const offset = RESIZE_HANDLE_SIZE / 2;
|
||||||
|
let positionStyle = "";
|
||||||
|
switch (pos) {
|
||||||
|
case "nw":
|
||||||
|
positionStyle = `left: -${offset}px; top: -${offset}px;`;
|
||||||
|
break;
|
||||||
|
case "n":
|
||||||
|
positionStyle = `left: calc(50% - ${offset}px); top: -${offset}px;`;
|
||||||
|
break;
|
||||||
|
case "ne":
|
||||||
|
positionStyle = `right: -${offset}px; top: -${offset}px;`;
|
||||||
|
break;
|
||||||
|
case "e":
|
||||||
|
positionStyle = `right: -${offset}px; top: calc(50% - ${offset}px);`;
|
||||||
|
break;
|
||||||
|
case "se":
|
||||||
|
positionStyle = `right: -${offset}px; bottom: -${offset}px;`;
|
||||||
|
break;
|
||||||
|
case "s":
|
||||||
|
positionStyle = `left: calc(50% - ${offset}px); bottom: -${offset}px;`;
|
||||||
|
break;
|
||||||
|
case "sw":
|
||||||
|
positionStyle = `left: -${offset}px; bottom: -${offset}px;`;
|
||||||
|
break;
|
||||||
|
case "w":
|
||||||
|
positionStyle = `left: -${offset}px; top: calc(50% - ${offset}px);`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const handle = createElement("div", {
|
||||||
|
class: `squire-resize-handle squire-resize-handle-${pos}`,
|
||||||
|
style: `
|
||||||
|
position: absolute;
|
||||||
|
width: ${RESIZE_HANDLE_SIZE}px;
|
||||||
|
height: ${RESIZE_HANDLE_SIZE}px;
|
||||||
|
background: #0067b9;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
cursor: ${cursor};
|
||||||
|
pointer-events: auto;
|
||||||
|
touch-action: none;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||||
|
${positionStyle}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
handle.addEventListener("pointerdown", this);
|
||||||
|
return {
|
||||||
|
element: handle,
|
||||||
|
cursor,
|
||||||
|
position: pos
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const resizeContainer = createElement(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "squire-image-resize-container",
|
||||||
|
style: "position: absolute; pointer-events: none; z-index: 1000;"
|
||||||
|
},
|
||||||
|
handles.map((handle) => handle.element)
|
||||||
|
);
|
||||||
|
this._currentImage = image;
|
||||||
|
this._resizeContainer = resizeContainer;
|
||||||
|
this._handles = handles;
|
||||||
|
this._root.appendChild(this._resizeContainer);
|
||||||
|
const naturalWidth = image.naturalWidth;
|
||||||
|
this._originalRatio = naturalWidth / image.naturalHeight;
|
||||||
|
this._maxWidth = Math.min(
|
||||||
|
naturalWidth * 2,
|
||||||
|
image.parentElement ? image.parentElement.offsetWidth : MAX_IMAGE_SIZE,
|
||||||
|
MAX_IMAGE_SIZE
|
||||||
|
);
|
||||||
|
this._positionResizeContainer();
|
||||||
|
image.tabIndex = -1;
|
||||||
|
image.focus();
|
||||||
|
document.addEventListener("keydown", this, true);
|
||||||
|
}
|
||||||
|
_positionResizeContainer() {
|
||||||
|
const resizeContainer = this._resizeContainer;
|
||||||
|
const root = this._root;
|
||||||
|
const currentImage = this._currentImage;
|
||||||
|
if (!resizeContainer || !currentImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rootRect = root.getBoundingClientRect();
|
||||||
|
const imageRect = currentImage.getBoundingClientRect();
|
||||||
|
const top = imageRect.top - rootRect.top + root.scrollTop;
|
||||||
|
const left = imageRect.left - rootRect.left + root.scrollLeft;
|
||||||
|
const width = imageRect.width;
|
||||||
|
const height = imageRect.height;
|
||||||
|
resizeContainer.style.top = top + "px";
|
||||||
|
resizeContainer.style.left = left + "px";
|
||||||
|
resizeContainer.style.width = width + "px";
|
||||||
|
resizeContainer.style.height = height + "px";
|
||||||
|
}
|
||||||
|
_onPointerDown(event) {
|
||||||
|
if (this._currentHandle || !this._handles) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target;
|
||||||
|
const currentHandle = this._handles.find((h) => h.element === target) || null;
|
||||||
|
if (!currentHandle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentImage = this._currentImage;
|
||||||
|
if (!currentImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
target.addEventListener("pointermove", this);
|
||||||
|
target.addEventListener("pointerup", this);
|
||||||
|
target.addEventListener("pointercancel", this);
|
||||||
|
target.setPointerCapture(event.pointerId);
|
||||||
|
this._currentHandle = currentHandle;
|
||||||
|
this._startX = event.clientX;
|
||||||
|
this._startY = event.clientY;
|
||||||
|
const style = getComputedStyle(currentImage);
|
||||||
|
this._startWidth = parseFloat(style.width);
|
||||||
|
this._startHeight = parseFloat(style.height);
|
||||||
|
document.body.style.cursor = currentHandle.cursor;
|
||||||
|
}
|
||||||
|
_onPointerMove(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const currentHandle = this._currentHandle;
|
||||||
|
const currentImage = this._currentImage;
|
||||||
|
if (!currentHandle || !currentImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const deltaX = event.clientX - this._startX;
|
||||||
|
const deltaY = event.clientY - this._startY;
|
||||||
|
const maxWidth = this._maxWidth;
|
||||||
|
const originalRatio = this._originalRatio;
|
||||||
|
let newWidth = this._startWidth;
|
||||||
|
let newHeight = this._startHeight;
|
||||||
|
switch (currentHandle.position) {
|
||||||
|
case "sw":
|
||||||
|
case "nw":
|
||||||
|
case "w":
|
||||||
|
newWidth -= 2 * deltaX;
|
||||||
|
break;
|
||||||
|
case "se":
|
||||||
|
case "ne":
|
||||||
|
case "e":
|
||||||
|
newWidth += 2 * deltaX;
|
||||||
|
break;
|
||||||
|
case "n":
|
||||||
|
newHeight -= deltaY;
|
||||||
|
newWidth = newHeight * originalRatio;
|
||||||
|
break;
|
||||||
|
case "s":
|
||||||
|
newHeight += deltaY;
|
||||||
|
newWidth = newHeight * originalRatio;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (newWidth < MIN_IMAGE_SIZE) {
|
||||||
|
newWidth = MIN_IMAGE_SIZE;
|
||||||
|
} else if (newWidth > maxWidth) {
|
||||||
|
newWidth = maxWidth;
|
||||||
|
}
|
||||||
|
const currentImageStyle = currentImage.style;
|
||||||
|
currentImageStyle.width = newWidth + "px";
|
||||||
|
currentImageStyle.height = "auto";
|
||||||
|
this._positionResizeContainer();
|
||||||
|
}
|
||||||
|
_onPointerUp(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const target = event.target;
|
||||||
|
if (target) {
|
||||||
|
target.removeEventListener("pointermove", this);
|
||||||
|
target.removeEventListener("pointerup", this);
|
||||||
|
target.removeEventListener("pointercancel", this);
|
||||||
|
}
|
||||||
|
this._currentHandle = null;
|
||||||
|
document.body.style.cursor = "";
|
||||||
|
}
|
||||||
|
_onKeyDown(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
const keyHandler = keyHandlers2[event.key];
|
||||||
|
if (!keyHandler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const image = this._currentImage;
|
||||||
|
if (!image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const editor = this._editor;
|
||||||
|
const root = this._root;
|
||||||
|
this._deselectImage();
|
||||||
|
const range = editor.getSelection();
|
||||||
|
range.selectNode(image);
|
||||||
|
keyHandler(editor, range, root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// source/Editor.ts
|
// source/Editor.ts
|
||||||
var Squire = class {
|
var Squire = class {
|
||||||
constructor(root, config) {
|
constructor(root, config) {
|
||||||
|
|
@ -1843,6 +2288,7 @@
|
||||||
this._mayHaveZWS = false;
|
this._mayHaveZWS = false;
|
||||||
this._lastAnchorNode = null;
|
this._lastAnchorNode = null;
|
||||||
this._lastFocusNode = null;
|
this._lastFocusNode = null;
|
||||||
|
this._lastPathRange = null;
|
||||||
this._path = "";
|
this._path = "";
|
||||||
this._events = /* @__PURE__ */ new Map();
|
this._events = /* @__PURE__ */ new Map();
|
||||||
this._undoIndex = -1;
|
this._undoIndex = -1;
|
||||||
|
|
@ -1861,6 +2307,9 @@
|
||||||
this.addEventListener("cut", _onCut);
|
this.addEventListener("cut", _onCut);
|
||||||
this.addEventListener("copy", _onCopy);
|
this.addEventListener("copy", _onCopy);
|
||||||
this.addEventListener("paste", _onPaste);
|
this.addEventListener("paste", _onPaste);
|
||||||
|
this._dragRange = null;
|
||||||
|
this.addEventListener("dragstart", _onDragStart);
|
||||||
|
this.addEventListener("dragend", _onDragEnd);
|
||||||
this.addEventListener("drop", _onDrop);
|
this.addEventListener("drop", _onDrop);
|
||||||
this.addEventListener(
|
this.addEventListener(
|
||||||
"keydown",
|
"keydown",
|
||||||
|
|
@ -1882,6 +2331,7 @@
|
||||||
"beforeinput",
|
"beforeinput",
|
||||||
this._beforeInput
|
this._beforeInput
|
||||||
);
|
);
|
||||||
|
this._imageResizer = new ImageResizer(root, this);
|
||||||
this.setHTML("");
|
this.setHTML("");
|
||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
|
|
@ -1889,6 +2339,7 @@
|
||||||
this.removeEventListener(type);
|
this.removeEventListener(type);
|
||||||
});
|
});
|
||||||
this._mutation.disconnect();
|
this._mutation.disconnect();
|
||||||
|
this._imageResizer.destroy();
|
||||||
this._undoIndex = -1;
|
this._undoIndex = -1;
|
||||||
this._undoStack = [];
|
this._undoStack = [];
|
||||||
this._undoStackLength = 0;
|
this._undoStackLength = 0;
|
||||||
|
|
@ -1963,7 +2414,7 @@
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.bold();
|
this.bold();
|
||||||
break;
|
break;
|
||||||
case "formaItalic":
|
case "formatItalic":
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.italic();
|
this.italic();
|
||||||
break;
|
break;
|
||||||
|
|
@ -2157,9 +2608,11 @@
|
||||||
}
|
}
|
||||||
_getRangeAndRemoveBookmark(range) {
|
_getRangeAndRemoveBookmark(range) {
|
||||||
const root = this._root;
|
const root = this._root;
|
||||||
const start = root.querySelector("#" + this.startSelectionId);
|
const starts = root.querySelectorAll("#" + this.startSelectionId);
|
||||||
const end = root.querySelector("#" + this.endSelectionId);
|
const ends = root.querySelectorAll("#" + this.endSelectionId);
|
||||||
if (start && end) {
|
const start = starts[0];
|
||||||
|
const end = ends[0];
|
||||||
|
if (start && end && !!(start.compareDocumentPosition(end) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||||
let startContainer = start.parentNode;
|
let startContainer = start.parentNode;
|
||||||
let endContainer = end.parentNode;
|
let endContainer = end.parentNode;
|
||||||
const startOffset = Array.from(startContainer.childNodes).indexOf(
|
const startOffset = Array.from(startContainer.childNodes).indexOf(
|
||||||
|
|
@ -2194,6 +2647,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < starts.length; i += 1) {
|
||||||
|
starts[i].remove();
|
||||||
|
}
|
||||||
|
for (let i = 0; i < ends.length; i += 1) {
|
||||||
|
ends[i].remove();
|
||||||
|
}
|
||||||
return range || null;
|
return range || null;
|
||||||
}
|
}
|
||||||
getSelection() {
|
getSelection() {
|
||||||
|
|
@ -2279,10 +2738,23 @@
|
||||||
}
|
}
|
||||||
_updatePathOnEvent() {
|
_updatePathOnEvent() {
|
||||||
if (this._isFocused) {
|
if (this._isFocused) {
|
||||||
this._updatePath(this.getSelection());
|
const lastPathRange = this._lastPathRange;
|
||||||
|
const selection = this.getSelection();
|
||||||
|
if (!lastPathRange || !lastPathRange.commonAncestorContainer.isConnected || selection.compareBoundaryPoints(
|
||||||
|
0,
|
||||||
|
// Range.START_TO_START,
|
||||||
|
lastPathRange
|
||||||
|
) !== 0 || selection.compareBoundaryPoints(
|
||||||
|
2,
|
||||||
|
//Range.END_TO_END
|
||||||
|
lastPathRange
|
||||||
|
) !== 0) {
|
||||||
|
this._updatePath(selection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_updatePath(range, force) {
|
_updatePath(range, force) {
|
||||||
|
this._lastPathRange = range.cloneRange();
|
||||||
const anchor = range.startContainer;
|
const anchor = range.startContainer;
|
||||||
const focus = range.endContainer;
|
const focus = range.endContainer;
|
||||||
let newPath;
|
let newPath;
|
||||||
|
|
@ -2422,9 +2894,16 @@
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
saveUndoState(range) {
|
saveUndoState(range) {
|
||||||
range || (range = this.getSelection());
|
let rangeIsFromSelection = false;
|
||||||
|
if (!range) {
|
||||||
|
range = this.getSelection();
|
||||||
|
rangeIsFromSelection = true;
|
||||||
|
}
|
||||||
this._recordUndoState(range, this._isInUndoState);
|
this._recordUndoState(range, this._isInUndoState);
|
||||||
this._getRangeAndRemoveBookmark(range);
|
this._getRangeAndRemoveBookmark(range);
|
||||||
|
if (rangeIsFromSelection) {
|
||||||
|
this.setSelection(range);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
undo() {
|
undo() {
|
||||||
|
|
@ -2484,14 +2963,26 @@
|
||||||
}
|
}
|
||||||
getHTML(withBookmark) {
|
getHTML(withBookmark) {
|
||||||
let range;
|
let range;
|
||||||
if (withBookmark) {
|
let html = "";
|
||||||
range = this.getSelection();
|
this.modifyDocument(() => {
|
||||||
this._saveRangeToBookmark(range);
|
if (withBookmark) {
|
||||||
}
|
range = this.getSelection();
|
||||||
const html = this._getRawHTML().replace(/\u200B/g, "");
|
this._saveRangeToBookmark(range);
|
||||||
if (withBookmark) {
|
}
|
||||||
this._getRangeAndRemoveBookmark(range);
|
const resizeContainer = this._root.querySelector(
|
||||||
}
|
".squire-image-resize-container"
|
||||||
|
);
|
||||||
|
if (resizeContainer) {
|
||||||
|
resizeContainer.remove();
|
||||||
|
}
|
||||||
|
html = this._getRawHTML().replace(/\u200B/g, "");
|
||||||
|
if (resizeContainer) {
|
||||||
|
this._root.append(resizeContainer);
|
||||||
|
}
|
||||||
|
if (withBookmark) {
|
||||||
|
this._getRangeAndRemoveBookmark(range);
|
||||||
|
}
|
||||||
|
});
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
setHTML(html) {
|
setHTML(html) {
|
||||||
|
|
@ -2640,20 +3131,6 @@
|
||||||
insertPlainText(plainText, isPaste) {
|
insertPlainText(plainText, isPaste) {
|
||||||
const range = this.getSelection();
|
const range = this.getSelection();
|
||||||
if (range.collapsed && getClosest(range.startContainer, this._root, "PRE")) {
|
if (range.collapsed && getClosest(range.startContainer, this._root, "PRE")) {
|
||||||
const startContainer = range.startContainer;
|
|
||||||
let offset = range.startOffset;
|
|
||||||
let textNode;
|
|
||||||
if (!startContainer || !(startContainer instanceof Text)) {
|
|
||||||
const text = document.createTextNode("");
|
|
||||||
startContainer.insertBefore(
|
|
||||||
text,
|
|
||||||
startContainer.childNodes[offset]
|
|
||||||
);
|
|
||||||
textNode = text;
|
|
||||||
offset = 0;
|
|
||||||
} else {
|
|
||||||
textNode = startContainer;
|
|
||||||
}
|
|
||||||
let doInsert = true;
|
let doInsert = true;
|
||||||
if (isPaste) {
|
if (isPaste) {
|
||||||
const event = new CustomEvent("willPaste", {
|
const event = new CustomEvent("willPaste", {
|
||||||
|
|
@ -2667,6 +3144,27 @@
|
||||||
doInsert = !event.defaultPrevented;
|
doInsert = !event.defaultPrevented;
|
||||||
}
|
}
|
||||||
if (doInsert) {
|
if (doInsert) {
|
||||||
|
this.saveUndoState(range);
|
||||||
|
const startContainer = range.startContainer;
|
||||||
|
let offset = range.startOffset;
|
||||||
|
let textNode;
|
||||||
|
if (startContainer instanceof Text) {
|
||||||
|
textNode = startContainer;
|
||||||
|
} else {
|
||||||
|
let nodeBeforeCursor = offset ? startContainer.childNodes[offset - 1] : null;
|
||||||
|
if (nodeBeforeCursor instanceof Text) {
|
||||||
|
textNode = nodeBeforeCursor;
|
||||||
|
offset = textNode.length;
|
||||||
|
} else {
|
||||||
|
const text = document.createTextNode("");
|
||||||
|
startContainer.insertBefore(
|
||||||
|
text,
|
||||||
|
startContainer.childNodes[offset]
|
||||||
|
);
|
||||||
|
textNode = text;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
textNode.insertData(offset, plainText);
|
textNode.insertData(offset, plainText);
|
||||||
range.setStart(textNode, offset + plainText.length);
|
range.setStart(textNode, offset + plainText.length);
|
||||||
range.collapse(true);
|
range.collapse(true);
|
||||||
|
|
@ -3038,8 +3536,11 @@
|
||||||
// --- Block formatting
|
// --- Block formatting
|
||||||
_ensureBottomLine() {
|
_ensureBottomLine() {
|
||||||
const root = this._root;
|
const root = this._root;
|
||||||
const last = root.lastElementChild;
|
let last = root;
|
||||||
if (!last || last.nodeName !== this._config.blockTag || !isBlock(last)) {
|
do {
|
||||||
|
last = last.lastElementChild;
|
||||||
|
} while (last && isContainer(last));
|
||||||
|
if (!last || !isBlock(last)) {
|
||||||
root.append(this.createDefaultBlock());
|
root.append(this.createDefaultBlock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3076,10 +3577,11 @@
|
||||||
if (block && (parent = getClosest(block, root, "PRE"))) {
|
if (block && (parent = getClosest(block, root, "PRE"))) {
|
||||||
moveRangeBoundariesDownTree(range);
|
moveRangeBoundariesDownTree(range);
|
||||||
node = range.startContainer;
|
node = range.startContainer;
|
||||||
const offset2 = range.startOffset;
|
let offset2 = range.startOffset;
|
||||||
if (!(node instanceof Text)) {
|
if (!(node instanceof Text)) {
|
||||||
node = document.createTextNode("");
|
node = document.createTextNode("");
|
||||||
parent.insertBefore(node, parent.firstChild);
|
parent.insertBefore(node, parent.firstChild);
|
||||||
|
offset2 = 0;
|
||||||
}
|
}
|
||||||
if (!lineBreakOnly && node instanceof Text && (node.data.charAt(offset2 - 1) === "\n" || rangeDoesStartAtBlockBoundary(range, root)) && (node.data.charAt(offset2) === "\n" || rangeDoesEndAtBlockBoundary(range, root))) {
|
if (!lineBreakOnly && node instanceof Text && (node.data.charAt(offset2 - 1) === "\n" || rangeDoesStartAtBlockBoundary(range, root)) && (node.data.charAt(offset2) === "\n" || rangeDoesEndAtBlockBoundary(range, root))) {
|
||||||
node.deleteData(offset2 && offset2 - 1, offset2 ? 2 : 1);
|
node.deleteData(offset2 && offset2 - 1, offset2 ? 2 : 1);
|
||||||
|
|
@ -3101,7 +3603,9 @@
|
||||||
range.setStart(node, 0);
|
range.setStart(node, 0);
|
||||||
} else {
|
} else {
|
||||||
node.insertData(offset2, "\n");
|
node.insertData(offset2, "\n");
|
||||||
fixCursor(parent);
|
if (!node.nextSibling) {
|
||||||
|
parent.append(createElement("BR"));
|
||||||
|
}
|
||||||
if (node.length === offset2 + 1) {
|
if (node.length === offset2 + 1) {
|
||||||
range.setStartAfter(node);
|
range.setStartAfter(node);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue