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
|
||||
var ELEMENT_NODE = 1;
|
||||
var TEXT_NODE = 3;
|
||||
var COMMENT_NODE = 8;
|
||||
var DOCUMENT_FRAGMENT_NODE = 11;
|
||||
var ZWS = "\u200B";
|
||||
var ua = navigator.userAgent;
|
||||
|
|
@ -55,7 +56,7 @@
|
|||
var isWebKit = /WebKit\//.test(ua);
|
||||
var ctrlKey = isMac || isIOS ? "Meta-" : "Ctrl-";
|
||||
var cantFocusEmptyTextNodes = isWebKit;
|
||||
var notWS = /[^ \t\r\n]/;
|
||||
var notWS = /[^ \t\r\n\u200B]/;
|
||||
|
||||
// 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)$/;
|
||||
|
|
@ -71,6 +72,7 @@
|
|||
var isLeaf = (node) => leafNodeNames.has(node.nodeName);
|
||||
var getNodeCategory = (node) => {
|
||||
switch (node.nodeType) {
|
||||
case COMMENT_NODE:
|
||||
case TEXT_NODE:
|
||||
return INLINE;
|
||||
case ELEMENT_NODE:
|
||||
|
|
@ -252,11 +254,12 @@
|
|||
return nodeStartAfterStart && nodeEndBeforeEnd;
|
||||
}
|
||||
};
|
||||
var canDescendInto = (node) => node instanceof Text || node.nodeType === ELEMENT_NODE && !isLeaf(node);
|
||||
var moveRangeBoundariesDownTree = (range) => {
|
||||
let { startContainer, startOffset, endContainer, endOffset } = range;
|
||||
while (!(startContainer instanceof Text)) {
|
||||
let child = startContainer.childNodes[startOffset];
|
||||
if (!child || isLeaf(child)) {
|
||||
if (!child || !canDescendInto(child)) {
|
||||
if (startOffset) {
|
||||
child = startContainer.childNodes[startOffset - 1];
|
||||
if (child instanceof Text) {
|
||||
|
|
@ -278,7 +281,7 @@
|
|||
if (endOffset) {
|
||||
while (!(endContainer instanceof Text)) {
|
||||
const child = endContainer.childNodes[endOffset - 1];
|
||||
if (!child || isLeaf(child)) {
|
||||
if (!child || !canDescendInto(child)) {
|
||||
if (child && child.nodeName === "BR" && !isLineBreak(child)) {
|
||||
--endOffset;
|
||||
continue;
|
||||
|
|
@ -291,7 +294,7 @@
|
|||
} else {
|
||||
while (!(endContainer instanceof Text)) {
|
||||
const child = endContainer.firstChild;
|
||||
if (!child || isLeaf(child)) {
|
||||
if (!child || !canDescendInto(child)) {
|
||||
break;
|
||||
}
|
||||
endContainer = child;
|
||||
|
|
@ -368,7 +371,7 @@
|
|||
replace(classNames, family) {
|
||||
return createElement("SPAN", {
|
||||
class: classNames.fontFamily,
|
||||
style: "font-family:" + family
|
||||
style: { fontFamily: family }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -377,7 +380,7 @@
|
|||
replace(classNames, size) {
|
||||
return createElement("SPAN", {
|
||||
class: classNames.fontSize,
|
||||
style: "font-size:" + size
|
||||
style: { fontSize: size }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -557,6 +560,9 @@
|
|||
};
|
||||
var fixContainer = (container) => {
|
||||
let wrapper = null;
|
||||
if (/^(?:TABLE|TBODY|TR|TH|TD|P)/.test(container.nodeName)) {
|
||||
return container;
|
||||
}
|
||||
[...container.childNodes].forEach((child) => {
|
||||
if (isInline(child)) {
|
||||
wrapper || (wrapper = createElement("DIV"));
|
||||
|
|
@ -566,10 +572,13 @@
|
|||
wrapper = null;
|
||||
}
|
||||
});
|
||||
wrapper && container.append(wrapper);
|
||||
wrapper && container.append(fixCursor(wrapper));
|
||||
return container;
|
||||
};
|
||||
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 (typeof offset !== "number") {
|
||||
throw new Error("Offset must be a number to split text node!");
|
||||
|
|
@ -1020,7 +1029,7 @@
|
|||
}
|
||||
moveRangeBoundariesDownTree(range);
|
||||
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 blockContentsAfterSplit = null;
|
||||
const firstBlockInFrag = getNextBlock(frag, frag);
|
||||
|
|
@ -1054,11 +1063,15 @@
|
|||
}
|
||||
}
|
||||
mergeWithBlock(container, firstBlockInFrag, range, root);
|
||||
offset = Array.from(container.parentNode.childNodes).indexOf(
|
||||
container
|
||||
) + 1;
|
||||
container = container.parentNode;
|
||||
range.setEnd(container, offset);
|
||||
if (container === root) {
|
||||
range.setEnd(root, getLength(root));
|
||||
} else {
|
||||
offset = Array.from(container.parentNode.childNodes).indexOf(
|
||||
container
|
||||
) + 1;
|
||||
container = container.parentNode;
|
||||
range.setEnd(container, offset);
|
||||
}
|
||||
}
|
||||
if (getLength(frag)) {
|
||||
if (replaceBlock && block) {
|
||||
|
|
@ -1066,6 +1079,13 @@
|
|||
range.collapse(false);
|
||||
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);
|
||||
let nodeAfterSplit = split(
|
||||
range.endContainer,
|
||||
|
|
@ -1149,29 +1169,25 @@
|
|||
|
||||
// source/Clipboard.ts
|
||||
var indexOf = Array.prototype.indexOf;
|
||||
var extractRangeToClipboard = (event, range, root, removeRangeFromDocument, toCleanHTML, toPlainText, plainTextOnly) => {
|
||||
const clipboardData = event.clipboardData;
|
||||
if (!clipboardData) {
|
||||
return false;
|
||||
}
|
||||
var extractRange = (range, root, removeRangeFromDocument, toCleanHTML, toPlainText) => {
|
||||
let text = toPlainText ? "" : getTextContentsOfRange(range);
|
||||
const startBlock = getStartBlockOfRange(range, root);
|
||||
const endBlock = getEndBlockOfRange(range, root);
|
||||
let parent = range.commonAncestorContainer;
|
||||
let copyRoot = root;
|
||||
if (startBlock === endBlock && startBlock?.contains(range.commonAncestorContainer)) {
|
||||
if (startBlock === endBlock && startBlock?.contains(parent)) {
|
||||
copyRoot = startBlock;
|
||||
}
|
||||
let contents;
|
||||
if (removeRangeFromDocument) {
|
||||
contents = deleteContentsOfRange(range, root);
|
||||
if (!parent.isConnected) {
|
||||
parent = range.commonAncestorContainer;
|
||||
}
|
||||
} else {
|
||||
range = range.cloneRange();
|
||||
moveRangeBoundariesDownTree(range);
|
||||
moveRangeBoundariesUpTree(range, copyRoot, copyRoot, root);
|
||||
contents = range.cloneContents();
|
||||
}
|
||||
let parent = range.commonAncestorContainer;
|
||||
if (parent instanceof Text) {
|
||||
while (parent && !(parent instanceof Element)) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
while (parent && parent !== copyRoot) {
|
||||
|
|
@ -1183,7 +1199,7 @@
|
|||
let html;
|
||||
if (contents.childNodes.length === 1 && contents.childNodes[0] instanceof Text) {
|
||||
text = contents.childNodes[0].data.replace(/ /g, " ");
|
||||
plainTextOnly = true;
|
||||
html = void 0;
|
||||
} else {
|
||||
const node = createElement("DIV");
|
||||
node.append(contents);
|
||||
|
|
@ -1195,15 +1211,34 @@
|
|||
if (toPlainText && html !== void 0) {
|
||||
text = toPlainText(html);
|
||||
}
|
||||
if (text === html) {
|
||||
html = void 0;
|
||||
}
|
||||
if (isWin) {
|
||||
text = text.replace(/\r?\n/g, "\r\n");
|
||||
}
|
||||
if (!plainTextOnly && html && text !== html) {
|
||||
if (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/plain", text);
|
||||
event.preventDefault();
|
||||
return true;
|
||||
};
|
||||
var _onCut = function(event) {
|
||||
|
|
@ -1251,7 +1286,11 @@
|
|||
var _onPaste = function(event) {
|
||||
const clipboardData = event.clipboardData;
|
||||
const items = clipboardData.items;
|
||||
const choosePlain = this._isShiftDown;
|
||||
const choosePlain = this._isShiftDown || !!getNearest(
|
||||
this.getSelection().commonAncestorContainer,
|
||||
this._root,
|
||||
"PRE"
|
||||
);
|
||||
let hasRTF = false;
|
||||
let hasImage = false;
|
||||
let plainItem = null;
|
||||
|
|
@ -1270,7 +1309,7 @@
|
|||
hasImage = true;
|
||||
}
|
||||
}
|
||||
if (hasImage && !(hasRTF && htmlItem)) {
|
||||
if (hasImage && !(hasRTF && htmlItem) && !plainItem) {
|
||||
event.preventDefault();
|
||||
this.fireEvent("pasteImage", {
|
||||
clipboardData
|
||||
|
|
@ -1284,42 +1323,122 @@
|
|||
});
|
||||
} else if (plainItem) {
|
||||
plainItem.getAsString((text) => {
|
||||
let isLink = false;
|
||||
const range = this.getSelection();
|
||||
if (!range.collapsed && notWS.test(range.toString())) {
|
||||
const match = this.linkRegExp.exec(text);
|
||||
isLink = !!match && match[0].length === text.length;
|
||||
}
|
||||
if (isLink) {
|
||||
this.makeLink(text);
|
||||
} else {
|
||||
this.insertPlainText(text, true);
|
||||
const isLink = !!match && match[0].length === text.length;
|
||||
if (isLink) {
|
||||
const href = match[1] ? /^(?:ht|f)tps?:/i.test(match[1]) ? match[1] : "http://" + match[1] : "mailto:" + match[0];
|
||||
this.makeLink(href);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (!event.dataTransfer) {
|
||||
const dataTransfer = event.dataTransfer;
|
||||
if (!dataTransfer) {
|
||||
return;
|
||||
}
|
||||
const types = event.dataTransfer.types;
|
||||
let l = types.length;
|
||||
const types = dataTransfer.types;
|
||||
let hasPlain = false;
|
||||
let hasHTML = false;
|
||||
while (l--) {
|
||||
switch (types[l]) {
|
||||
for (let i = 0, l = types.length; i < l; i += 1) {
|
||||
switch (types[i]) {
|
||||
case "text/plain":
|
||||
hasPlain = true;
|
||||
break;
|
||||
case "text/html":
|
||||
hasHTML = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (hasHTML || hasPlain && this.saveUndoState) {
|
||||
this.saveUndoState();
|
||||
if (!hasPlain && !hasHTML) {
|
||||
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;
|
||||
const range = this.getSelection();
|
||||
if (this._keyHandlers[key]) {
|
||||
this._keyHandlers[key](this, event, range);
|
||||
const handler = this._keyHandlers[key];
|
||||
if (handler) {
|
||||
handler(this, event, range);
|
||||
} else if (!range.collapsed && !event.ctrlKey && !event.metaKey && key.length === 1) {
|
||||
this.saveUndoState(range);
|
||||
deleteContentsOfRange(range, this._root);
|
||||
|
|
@ -1667,7 +1787,7 @@
|
|||
do {
|
||||
if (node.nodeName === "CODE") {
|
||||
let next = node.nextSibling;
|
||||
if (!(next instanceof Text)) {
|
||||
if (!(next instanceof Text) || next.length === 0) {
|
||||
const textNode = document.createTextNode("\xA0");
|
||||
node.parentNode.insertBefore(textNode, next);
|
||||
next = textNode;
|
||||
|
|
@ -1759,6 +1879,331 @@
|
|||
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
|
||||
var Squire = class {
|
||||
constructor(root, config) {
|
||||
|
|
@ -1843,6 +2288,7 @@
|
|||
this._mayHaveZWS = false;
|
||||
this._lastAnchorNode = null;
|
||||
this._lastFocusNode = null;
|
||||
this._lastPathRange = null;
|
||||
this._path = "";
|
||||
this._events = /* @__PURE__ */ new Map();
|
||||
this._undoIndex = -1;
|
||||
|
|
@ -1861,6 +2307,9 @@
|
|||
this.addEventListener("cut", _onCut);
|
||||
this.addEventListener("copy", _onCopy);
|
||||
this.addEventListener("paste", _onPaste);
|
||||
this._dragRange = null;
|
||||
this.addEventListener("dragstart", _onDragStart);
|
||||
this.addEventListener("dragend", _onDragEnd);
|
||||
this.addEventListener("drop", _onDrop);
|
||||
this.addEventListener(
|
||||
"keydown",
|
||||
|
|
@ -1882,6 +2331,7 @@
|
|||
"beforeinput",
|
||||
this._beforeInput
|
||||
);
|
||||
this._imageResizer = new ImageResizer(root, this);
|
||||
this.setHTML("");
|
||||
}
|
||||
destroy() {
|
||||
|
|
@ -1889,6 +2339,7 @@
|
|||
this.removeEventListener(type);
|
||||
});
|
||||
this._mutation.disconnect();
|
||||
this._imageResizer.destroy();
|
||||
this._undoIndex = -1;
|
||||
this._undoStack = [];
|
||||
this._undoStackLength = 0;
|
||||
|
|
@ -1963,7 +2414,7 @@
|
|||
event.preventDefault();
|
||||
this.bold();
|
||||
break;
|
||||
case "formaItalic":
|
||||
case "formatItalic":
|
||||
event.preventDefault();
|
||||
this.italic();
|
||||
break;
|
||||
|
|
@ -2157,9 +2608,11 @@
|
|||
}
|
||||
_getRangeAndRemoveBookmark(range) {
|
||||
const root = this._root;
|
||||
const start = root.querySelector("#" + this.startSelectionId);
|
||||
const end = root.querySelector("#" + this.endSelectionId);
|
||||
if (start && end) {
|
||||
const starts = root.querySelectorAll("#" + this.startSelectionId);
|
||||
const ends = root.querySelectorAll("#" + this.endSelectionId);
|
||||
const start = starts[0];
|
||||
const end = ends[0];
|
||||
if (start && end && !!(start.compareDocumentPosition(end) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
let startContainer = start.parentNode;
|
||||
let endContainer = end.parentNode;
|
||||
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;
|
||||
}
|
||||
getSelection() {
|
||||
|
|
@ -2279,10 +2738,23 @@
|
|||
}
|
||||
_updatePathOnEvent() {
|
||||
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) {
|
||||
this._lastPathRange = range.cloneRange();
|
||||
const anchor = range.startContainer;
|
||||
const focus = range.endContainer;
|
||||
let newPath;
|
||||
|
|
@ -2422,9 +2894,16 @@
|
|||
return this;
|
||||
}
|
||||
saveUndoState(range) {
|
||||
range || (range = this.getSelection());
|
||||
let rangeIsFromSelection = false;
|
||||
if (!range) {
|
||||
range = this.getSelection();
|
||||
rangeIsFromSelection = true;
|
||||
}
|
||||
this._recordUndoState(range, this._isInUndoState);
|
||||
this._getRangeAndRemoveBookmark(range);
|
||||
if (rangeIsFromSelection) {
|
||||
this.setSelection(range);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
undo() {
|
||||
|
|
@ -2484,14 +2963,26 @@
|
|||
}
|
||||
getHTML(withBookmark) {
|
||||
let range;
|
||||
if (withBookmark) {
|
||||
range = this.getSelection();
|
||||
this._saveRangeToBookmark(range);
|
||||
}
|
||||
const html = this._getRawHTML().replace(/\u200B/g, "");
|
||||
if (withBookmark) {
|
||||
this._getRangeAndRemoveBookmark(range);
|
||||
}
|
||||
let html = "";
|
||||
this.modifyDocument(() => {
|
||||
if (withBookmark) {
|
||||
range = this.getSelection();
|
||||
this._saveRangeToBookmark(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;
|
||||
}
|
||||
setHTML(html) {
|
||||
|
|
@ -2640,20 +3131,6 @@
|
|||
insertPlainText(plainText, isPaste) {
|
||||
const range = this.getSelection();
|
||||
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;
|
||||
if (isPaste) {
|
||||
const event = new CustomEvent("willPaste", {
|
||||
|
|
@ -2667,6 +3144,27 @@
|
|||
doInsert = !event.defaultPrevented;
|
||||
}
|
||||
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);
|
||||
range.setStart(textNode, offset + plainText.length);
|
||||
range.collapse(true);
|
||||
|
|
@ -3038,8 +3536,11 @@
|
|||
// --- Block formatting
|
||||
_ensureBottomLine() {
|
||||
const root = this._root;
|
||||
const last = root.lastElementChild;
|
||||
if (!last || last.nodeName !== this._config.blockTag || !isBlock(last)) {
|
||||
let last = root;
|
||||
do {
|
||||
last = last.lastElementChild;
|
||||
} while (last && isContainer(last));
|
||||
if (!last || !isBlock(last)) {
|
||||
root.append(this.createDefaultBlock());
|
||||
}
|
||||
}
|
||||
|
|
@ -3076,10 +3577,11 @@
|
|||
if (block && (parent = getClosest(block, root, "PRE"))) {
|
||||
moveRangeBoundariesDownTree(range);
|
||||
node = range.startContainer;
|
||||
const offset2 = range.startOffset;
|
||||
let offset2 = range.startOffset;
|
||||
if (!(node instanceof Text)) {
|
||||
node = document.createTextNode("");
|
||||
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))) {
|
||||
node.deleteData(offset2 && offset2 - 1, offset2 ? 2 : 1);
|
||||
|
|
@ -3101,7 +3603,9 @@
|
|||
range.setStart(node, 0);
|
||||
} else {
|
||||
node.insertData(offset2, "\n");
|
||||
fixCursor(parent);
|
||||
if (!node.nextSibling) {
|
||||
parent.append(createElement("BR"));
|
||||
}
|
||||
if (node.length === offset2 + 1) {
|
||||
range.setStartAfter(node);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue