diff --git a/plugins/compact-composer/index.php b/plugins/compact-composer/index.php
index e40ab5d2ac..ae743ca792 100644
--- a/plugins/compact-composer/index.php
+++ b/plugins/compact-composer/index.php
@@ -6,7 +6,7 @@ class CompactComposerPlugin extends \RainLoop\Plugins\AbstractPlugin
NAME = 'Compact Composer',
AUTHOR = 'Sergey Mosin',
URL = 'https://github.com/the-djmaze/snappymail/pull/1466',
- VERSION = '1.0.3',
+ VERSION = '1.0.4',
RELEASE = '2024-04-23',
REQUIRED = '2.34.0',
LICENSE = 'AGPL v3',
diff --git a/plugins/compact-composer/js/CompactComposer.js b/plugins/compact-composer/js/CompactComposer.js
index c94377c2b0..288fb497c7 100644
--- a/plugins/compact-composer/js/CompactComposer.js
+++ b/plugins/compact-composer/js/CompactComposer.js
@@ -155,6 +155,20 @@
pasteImageHandler(e, squire);
});
+ wysiwyg.addEventListener('focus', () => {
+ const range = this.squire.getSelection();
+ if (range.collapsed && range.startContainer === wysiwyg) {
+ // when the caret is directly in the wysiwyg a bunch of stuff
+ // (like lists, blockquotes, etc...) do not work,
+ // so we need to place it inside the nearest element
+ if (wysiwyg.children[range.startOffset] !== undefined) {
+ const newRange = document.createRange();
+ newRange.setStart(wysiwyg.children[range.startOffset], 0);
+ this.squire.setSelection(newRange);
+ }
+ }
+ });
+
// squire.addEventListener('focus', () => shortcuts.off());
// squire.addEventListener('blur', () => shortcuts.on());
@@ -598,7 +612,7 @@
type: 'menu_item',
label: 'HTML Mode',
id: 'menu-item-mode-wysiwyg',
- cmd: () => this.setMode('wysiwyg'),
+ cmd: () => this.setModeCmd('wysiwyg'),
showInPlainMode: true,
icon: ''
},
@@ -606,7 +620,7 @@
type: 'menu_item',
label: 'Edit Source',
id: 'menu-item-mode-source',
- cmd: () => this.setMode('source'),
+ cmd: () => this.setModeCmd('source'),
showInPlainMode: true,
icon: ''
},
@@ -614,7 +628,7 @@
type: 'menu_item',
label: 'Plain Text Mode',
id: 'menu-item-mode-plain',
- cmd: () => this.setMode('plain'),
+ cmd: () => this.setModeCmd('plain'),
showInPlainMode: true,
icon: ''
}
@@ -871,6 +885,12 @@
return validation.test(this.squire.getPath()) || this.squire.hasFormat(format);
}
*/
+
+ setModeCmd(mode) {
+ this.setMode(mode);
+ setTimeout(() => this.focus(), 1);
+ }
+
setMode(mode) {
if (this.mode !== mode) {
let cl = this.container.classList,
@@ -894,7 +914,6 @@
this.mode = mode;
cl.add('squire2-mode-' + mode);
this.onModeChange?.();
- setTimeout(() => this.focus(), 1);
}
}
diff --git a/plugins/compact-composer/js/squire-raw.js b/plugins/compact-composer/js/squire-raw.js
index 1f079a9a6e..949efa7d7a 100644
--- a/plugins/compact-composer/js/squire-raw.js
+++ b/plugins/compact-composer/js/squire-raw.js
@@ -1,5 +1,5 @@
-"use strict";
-// v2.2.7
+'use strict';
+// v2.2.8
(() => {
// source/node/TreeIterator.ts
var SHOW_ELEMENT = 1;
@@ -13,11 +13,13 @@
this.nodeType = nodeType;
this.filter = filter || always;
}
+
isAcceptableNode(node) {
const nodeType = node.nodeType;
const nodeFilterType = nodeType === Node.ELEMENT_NODE ? SHOW_ELEMENT : nodeType === Node.TEXT_NODE ? SHOW_TEXT : 0;
return !!(nodeFilterType & this.nodeType) && this.filter(node);
}
+
nextNode() {
const root = this.root;
let current = this.currentNode;
@@ -43,6 +45,7 @@
current = node;
}
}
+
previousNode() {
const root = this.root;
let current = this.currentNode;
@@ -69,6 +72,7 @@
current = node;
}
}
+
// Previous node in post-order.
previousPONode() {
const root = this.root;
@@ -101,7 +105,7 @@
var ELEMENT_NODE = 1;
var TEXT_NODE = 3;
var DOCUMENT_FRAGMENT_NODE = 11;
- var ZWS = "\u200B";
+ var ZWS = '\u200B';
var ua = navigator.userAgent;
var isMac = /Mac OS X/.test(ua);
var isWin = /Windows NT/.test(ua);
@@ -110,14 +114,14 @@
var isGecko = /Gecko\//.test(ua);
var isLegacyEdge = /Edge\//.test(ua);
var isWebKit = !isLegacyEdge && /WebKit\//.test(ua);
- var ctrlKey = isMac || isIOS ? "Meta-" : "Ctrl-";
+ var ctrlKey = isMac || isIOS ? 'Meta-' : 'Ctrl-';
var cantFocusEmptyTextNodes = isWebKit;
- var supportsInputEvents = "onbeforeinput" in document && "inputType" in new InputEvent("input");
+ var supportsInputEvents = 'onbeforeinput' in document && 'inputType' in new InputEvent('input');
var notWS = /[^ \t\r\n]/;
// 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(?:FRAME|MG|NPUT|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:AMP|MALL|PAN|TR(?:IKE|ONG)|U[BP])?|TIME|U|VAR|WBR)$/;
- var leafNodeNames = /* @__PURE__ */ new Set(["BR", "HR", "IFRAME", "IMG", "INPUT"]);
+ var leafNodeNames = /* @__PURE__ */ new Set(['BR', 'HR', 'IFRAME', 'IMG', 'INPUT']);
var UNKNOWN = 0;
var INLINE = 1;
var BLOCK = 2;
@@ -191,7 +195,7 @@
return false;
}
if (node instanceof HTMLElement && node2 instanceof HTMLElement) {
- return node.nodeName !== "A" && node.className === node2.className && node.style.cssText === node2.style.cssText;
+ return node.nodeName !== 'A' && node.className === node2.className && node.style.cssText === node2.style.cssText;
}
return true;
};
@@ -200,7 +204,7 @@
return false;
}
for (const attr in attributes) {
- if (!("getAttribute" in node) || node.getAttribute(attr) !== attributes[attr]) {
+ if (!('getAttribute' in node) || node.getAttribute(attr) !== attributes[attr]) {
return false;
}
}
@@ -269,7 +273,7 @@
// source/node/Whitespace.ts
var notWSTextNode = (node) => {
- return node instanceof Element ? node.nodeName === "BR" : (
+ return node instanceof Element ? node.nodeName === 'BR' : (
// okay if data is 'undefined' here.
notWS.test(node.data)
);
@@ -359,7 +363,7 @@
while (!(endContainer instanceof Text)) {
const child = endContainer.childNodes[endOffset - 1];
if (!child || isLeaf(child)) {
- if (child && child.nodeName === "BR" && !isLineBreak(child, false)) {
+ if (child && child.nodeName === 'BR' && !isLineBreak(child, false)) {
endOffset -= 1;
continue;
}
@@ -403,7 +407,7 @@
if (endContainer === endMax || endContainer === root) {
break;
}
- if (endContainer.nodeType !== TEXT_NODE && endContainer.childNodes[endOffset] && endContainer.childNodes[endOffset].nodeName === "BR" && !isLineBreak(endContainer.childNodes[endOffset], false)) {
+ if (endContainer.nodeType !== TEXT_NODE && endContainer.childNodes[endOffset] && endContainer.childNodes[endOffset].nodeName === 'BR' && !isLineBreak(endContainer.childNodes[endOffset], false)) {
endOffset += 1;
}
if (endOffset !== getLength(endContainer)) {
@@ -447,11 +451,11 @@
if (cantFocusEmptyTextNodes) {
fixer = document.createTextNode(ZWS);
} else {
- fixer = document.createTextNode("");
+ fixer = document.createTextNode('');
}
}
- } else if ((node instanceof Element || node instanceof DocumentFragment) && !node.querySelector("BR")) {
- fixer = createElement("BR");
+ } else if ((node instanceof Element || node instanceof DocumentFragment) && !node.querySelector('BR')) {
+ fixer = createElement('BR');
let parent = node;
let child;
while ((child = parent.lastElementChild) && !isInline(child)) {
@@ -470,15 +474,15 @@
var fixContainer = (container, root) => {
let wrapper = null;
Array.from(container.childNodes).forEach((child) => {
- const isBR = child.nodeName === "BR";
+ const isBR = child.nodeName === 'BR';
if (!isBR && isInline(child)) {
if (!wrapper) {
- wrapper = createElement("DIV");
+ wrapper = createElement('DIV');
}
wrapper.appendChild(child);
} else if (isBR || wrapper) {
if (!wrapper) {
- wrapper = createElement("DIV");
+ wrapper = createElement('DIV');
}
fixCursor(wrapper);
if (isBR) {
@@ -499,15 +503,15 @@
};
var split = (node, offset, stopNode, root) => {
if (node instanceof Text && node !== stopNode) {
- if (typeof offset !== "number") {
- throw new Error("Offset must be a number to split text node!");
+ if (typeof offset !== 'number') {
+ throw new Error('Offset must be a number to split text node!');
}
if (!node.parentNode) {
- throw new Error("Cannot split text node with no parent!");
+ throw new Error('Cannot split text node with no parent!');
}
return split(node.parentNode, node.splitText(offset), stopNode, root);
}
- let nodeAfterSplit = typeof offset === "number" ? offset < node.childNodes.length ? node.childNodes[offset] : null : offset;
+ let nodeAfterSplit = typeof offset === 'number' ? offset < node.childNodes.length ? node.childNodes[offset] : null : offset;
const parent = node.parentNode;
if (!parent || node === stopNode || !(node instanceof Element)) {
return nodeAfterSplit;
@@ -518,7 +522,7 @@
clone.appendChild(nodeAfterSplit);
nodeAfterSplit = next;
}
- if (node instanceof HTMLOListElement && getNearest(node, root, "BLOCKQUOTE")) {
+ if (node instanceof HTMLOListElement && getNearest(node, root, 'BLOCKQUOTE')) {
clone.start = (+node.start || 1) + node.childNodes.length - 1;
}
fixCursor(node);
@@ -597,7 +601,7 @@
detach(container);
offset = block.childNodes.length;
const last = block.lastChild;
- if (last && last.nodeName === "BR") {
+ if (last && last.nodeName === 'BR') {
block.removeChild(last);
offset -= 1;
}
@@ -609,14 +613,14 @@
var mergeContainers = (node, root) => {
const prev = node.previousSibling;
const first = node.firstChild;
- const isListItem = node.nodeName === "LI";
+ const isListItem = node.nodeName === 'LI';
if (isListItem && (!first || !/^[OU]L$/.test(first.nodeName))) {
return;
}
if (prev && areAlike(prev, node)) {
if (!isContainer(prev)) {
if (isListItem) {
- const block = createElement("DIV");
+ const block = createElement('DIV');
block.appendChild(empty(prev));
prev.appendChild(block);
} else {
@@ -633,7 +637,7 @@
mergeContainers(first, root);
}
} else if (isListItem) {
- const block = createElement("DIV");
+ const block = createElement('DIV');
node.insertBefore(block, first);
fixCursor(block);
}
@@ -641,40 +645,40 @@
// source/Clean.ts
var styleToSemantic = {
- "font-weight": {
+ 'font-weight': {
regexp: /^bold|^700/i,
replace() {
- return createElement("B");
+ return createElement('B');
}
},
- "font-style": {
+ 'font-style': {
regexp: /^italic/i,
replace() {
- return createElement("I");
+ return createElement('I');
}
},
- "font-family": {
+ 'font-family': {
regexp: notWS,
replace(classNames, family) {
- return createElement("SPAN", {
+ return createElement('SPAN', {
class: classNames.fontFamily,
- style: "font-family:" + family
+ style: 'font-family:' + family
});
}
},
- "font-size": {
+ 'font-size': {
regexp: notWS,
replace(classNames, size) {
- return createElement("SPAN", {
+ return createElement('SPAN', {
class: classNames.fontSize,
- style: "font-size:" + size
+ style: 'font-size:' + size
});
}
},
- "text-decoration": {
+ 'text-decoration': {
regexp: /^underline/i,
replace() {
- return createElement("U");
+ return createElement('U');
}
}
};
@@ -724,19 +728,19 @@
};
};
var fontSizes = {
- "1": "10",
- "2": "13",
- "3": "16",
- "4": "18",
- "5": "24",
- "6": "32",
- "7": "48"
+ '1': '10',
+ '2': '13',
+ '3': '16',
+ '4': '18',
+ '5': '24',
+ '6': '32',
+ '7': '48'
};
var stylesRewriters = {
- STRONG: replaceWithTag("B"),
- EM: replaceWithTag("I"),
- INS: replaceWithTag("U"),
- STRIKE: replaceWithTag("S"),
+ STRONG: replaceWithTag('B'),
+ EM: replaceWithTag('I'),
+ INS: replaceWithTag('U'),
+ STRIKE: replaceWithTag('S'),
SPAN: replaceStyles,
FONT: (node, parent, config) => {
const font = node;
@@ -750,17 +754,17 @@
let newTreeBottom;
let newTreeTop;
if (face) {
- fontSpan = createElement("SPAN", {
+ fontSpan = createElement('SPAN', {
class: classNames.fontFamily,
- style: "font-family:" + face
+ style: 'font-family:' + face
});
newTreeTop = fontSpan;
newTreeBottom = fontSpan;
}
if (size) {
- sizeSpan = createElement("SPAN", {
+ sizeSpan = createElement('SPAN', {
class: classNames.fontSize,
- style: "font-size:" + fontSizes[size] + "px"
+ style: 'font-size:' + fontSizes[size] + 'px'
});
if (!newTreeTop) {
newTreeTop = sizeSpan;
@@ -771,12 +775,12 @@
newTreeBottom = sizeSpan;
}
if (color && /^#?([\dA-F]{3}){1,2}$/i.test(color)) {
- if (color.charAt(0) !== "#") {
- color = "#" + color;
+ if (color.charAt(0) !== '#') {
+ color = '#' + color;
}
- colorSpan = createElement("SPAN", {
+ colorSpan = createElement('SPAN', {
class: classNames.color,
- style: "color:" + color
+ style: 'color:' + color
});
if (!newTreeTop) {
newTreeTop = colorSpan;
@@ -787,14 +791,14 @@
newTreeBottom = colorSpan;
}
if (!newTreeTop || !newTreeBottom) {
- newTreeTop = newTreeBottom = createElement("SPAN");
+ newTreeTop = newTreeBottom = createElement('SPAN');
}
parent.replaceChild(newTreeTop, font);
newTreeBottom.appendChild(empty(font));
return newTreeBottom;
},
TT: (node, parent, config) => {
- const el = createElement("SPAN", {
+ const el = createElement('SPAN', {
class: config.classNames.fontFamily,
style: 'font-family:menlo,consolas,"courier new",monospace'
});
@@ -835,7 +839,7 @@
continue;
}
if (childLength) {
- cleanTree(child, config, preserveWS || nodeName === "PRE");
+ cleanTree(child, config, preserveWS || nodeName === 'PRE');
}
} else {
if (child instanceof Text) {
@@ -849,7 +853,7 @@
walker.currentNode = child;
let sibling;
while (sibling = walker.previousPONode()) {
- if (sibling.nodeName === "IMG" || sibling instanceof Text && notWS.test(sibling.data)) {
+ if (sibling.nodeName === 'IMG' || sibling instanceof Text && notWS.test(sibling.data)) {
break;
}
if (!isInline(sibling)) {
@@ -857,13 +861,13 @@
break;
}
}
- data = data.replace(/^[ \t\r\n]+/g, sibling ? " " : "");
+ data = data.replace(/^[ \t\r\n]+/g, sibling ? ' ' : '');
}
if (endsWithWS) {
walker.currentNode = child;
let sibling;
while (sibling = walker.nextNode()) {
- if (sibling.nodeName === "IMG" || sibling instanceof Text && notWS.test(sibling.data)) {
+ if (sibling.nodeName === 'IMG' || sibling instanceof Text && notWS.test(sibling.data)) {
break;
}
if (!isInline(sibling)) {
@@ -871,7 +875,7 @@
break;
}
}
- data = data.replace(/[ \t\r\n]+$/g, sibling ? " " : "");
+ data = data.replace(/[ \t\r\n]+$/g, sibling ? ' ' : '');
}
if (data) {
child.data = data;
@@ -901,7 +905,7 @@
}
};
var cleanupBRs = (node, root, keepForBlankLine) => {
- const brs = node.querySelectorAll("BR");
+ const brs = node.querySelectorAll('BR');
const brBreaksLine = [];
let l = brs.length;
for (let i = 0; i < l; i += 1) {
@@ -921,7 +925,7 @@
}
};
var escapeHTML = (text) => {
- return text.split("&").join("&").split("<").join("<").split(">").join(">").split('"').join(""");
+ return text.split('&').join('&').split('<').join('<').split('>').join('>').split('"').join('"');
};
// source/node/Block.ts
@@ -939,7 +943,7 @@
return block !== root ? block : null;
};
var isEmptyBlock = (block) => {
- return !block.textContent && !block.querySelector("IMG");
+ return !block.textContent && !block.querySelector('IMG');
};
// source/range/Block.ts
@@ -977,7 +981,7 @@
return block && isNodeContainedInRange(range, block, true) ? block : null;
};
var isContent = (node) => {
- return node instanceof Text ? notWS.test(node.data) : node.nodeName === "IMG";
+ return node instanceof Text ? notWS.test(node.data) : node.nodeName === 'IMG';
};
var rangeDoesStartAtBlockBoundary = (range, root) => {
const startContainer = range.startContainer;
@@ -1059,13 +1063,14 @@
function createRange(startContainer, startOffset, endContainer, endOffset) {
const range = document.createRange();
range.setStart(startContainer, startOffset);
- if (endContainer && typeof endOffset === "number") {
+ if (endContainer && typeof endOffset === 'number') {
range.setEnd(endContainer, endOffset);
} else {
range.setEnd(startContainer, startOffset);
}
return range;
}
+
var insertNodeInRange = (range, node) => {
let { startContainer, startOffset, endContainer, endOffset } = range;
let children;
@@ -1178,7 +1183,7 @@
fixCursor(startBlock);
}
const child = root.firstChild;
- if (!child || child.nodeName === "BR") {
+ if (!child || child.nodeName === 'BR') {
fixCursor(root);
if (root.firstChild) {
range.selectNodeContents(root.firstChild);
@@ -1191,7 +1196,7 @@
let afterNode = startContainer;
let afterOffset = startOffset;
if (!(afterNode instanceof Text) || afterOffset === afterNode.data.length) {
- afterNode = getAdjacentInlineNode(iterator, "nextNode", afterNode);
+ afterNode = getAdjacentInlineNode(iterator, 'nextNode', afterNode);
afterOffset = 0;
}
let beforeNode = startContainer;
@@ -1199,7 +1204,7 @@
if (!(beforeNode instanceof Text) || beforeOffset === -1) {
beforeNode = getAdjacentInlineNode(
iterator,
- "previousPONode",
+ 'previousPONode',
afterNode || (startContainer instanceof Text ? startContainer : startContainer.childNodes[startOffset] || startContainer)
);
if (beforeNode instanceof Text) {
@@ -1208,17 +1213,17 @@
}
let node = null;
let offset = 0;
- if (afterNode instanceof Text && afterNode.data.charAt(afterOffset) === " " && rangeDoesStartAtBlockBoundary(range, root)) {
+ if (afterNode instanceof Text && afterNode.data.charAt(afterOffset) === ' ' && rangeDoesStartAtBlockBoundary(range, root)) {
node = afterNode;
offset = afterOffset;
- } else if (beforeNode instanceof Text && beforeNode.data.charAt(beforeOffset) === " ") {
- if (afterNode instanceof Text && afterNode.data.charAt(afterOffset) === " " || rangeDoesEndAtBlockBoundary(range, root)) {
+ } else if (beforeNode instanceof Text && beforeNode.data.charAt(beforeOffset) === ' ') {
+ if (afterNode instanceof Text && afterNode.data.charAt(afterOffset) === ' ' || rangeDoesEndAtBlockBoundary(range, root)) {
node = beforeNode;
offset = beforeOffset;
}
}
if (node) {
- node.replaceData(offset, 1, "\xA0");
+ node.replaceData(offset, 1, '\xA0');
}
range.setStart(startContainer, startOffset);
range.collapse(true);
@@ -1237,13 +1242,13 @@
}
moveRangeBoundariesDownTree(range);
range.collapse(false);
- const stopPoint = getNearest(range.endContainer, root, "BLOCKQUOTE") || root;
+ const stopPoint = getNearest(range.endContainer, root, 'BLOCKQUOTE') || root;
let block = getStartBlockOfRange(range, root);
let blockContentsAfterSplit = null;
const firstBlockInFrag = getNextBlock(frag, frag);
const replaceBlock = !firstInFragIsInline && !!block && isEmptyBlock(block);
if (block && firstBlockInFrag && !replaceBlock && // Don't merge table cells or PRE elements into block
- !getNearest(firstBlockInFrag, frag, "PRE") && !getNearest(firstBlockInFrag, frag, "TABLE")) {
+ !getNearest(firstBlockInFrag, frag, 'PRE') && !getNearest(firstBlockInFrag, frag, 'TABLE')) {
moveRangeBoundariesUpTree(range, block, block, root);
range.collapse(true);
let container = range.endContainer;
@@ -1322,7 +1327,7 @@
// source/range/Contents.ts
var getTextContentsOfRange = (range) => {
if (range.collapsed) {
- return "";
+ return '';
}
const startContainer = range.startContainer;
const endContainer = range.endContainer;
@@ -1335,7 +1340,7 @@
);
walker.currentNode = startContainer;
let node = startContainer;
- let textContent = "";
+ let textContent = '';
let addedTextInBlock = false;
let value;
if (!(node instanceof Element) && !(node instanceof Text) || !walker.filter(node)) {
@@ -1354,13 +1359,13 @@
textContent += value;
addedTextInBlock = true;
}
- } else if (node.nodeName === "BR" || addedTextInBlock && !isInline(node)) {
- textContent += "\n";
+ } else if (node.nodeName === 'BR' || addedTextInBlock && !isInline(node)) {
+ textContent += '\n';
addedTextInBlock = false;
}
node = walker.nextNode();
}
- textContent = textContent.replace(/ /g, " ");
+ textContent = textContent.replace(/ /g, ' ');
return textContent;
};
@@ -1371,7 +1376,7 @@
if (isLegacyEdge || !clipboardData) {
return false;
}
- let text = toPlainText ? "" : getTextContentsOfRange(range);
+ let text = toPlainText ? '' : getTextContentsOfRange(range);
const startBlock = getStartBlockOfRange(range, root);
const endBlock = getEndBlockOfRange(range, root);
let copyRoot = root;
@@ -1399,10 +1404,10 @@
}
let html;
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;
} else {
- const node = createElement("DIV");
+ const node = createElement('DIV');
node.appendChild(contents);
html = node.innerHTML;
if (toCleanHTML) {
@@ -1413,12 +1418,12 @@
text = toPlainText(html);
}
if (isWin) {
- text = text.replace(/\r?\n/g, "\r\n");
+ text = text.replace(/\r?\n/g, '\r\n');
}
if (!plainTextOnly && html && text !== 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;
};
@@ -1477,11 +1482,11 @@
while (l--) {
const item = items[l];
const type = item.type;
- if (type === "text/html") {
+ if (type === 'text/html') {
htmlItem = item;
- } else if (type === "text/plain" || type === "text/uri-list") {
+ } else if (type === 'text/plain' || type === 'text/uri-list') {
plainItem = item;
- } else if (type === "text/rtf") {
+ } else if (type === 'text/rtf') {
hasRTF = true;
} else if (/^image\/.*/.test(type)) {
hasImage = true;
@@ -1489,7 +1494,7 @@
}
if (hasImage && !(hasRTF && htmlItem)) {
event.preventDefault();
- this.fireEvent("pasteImage", {
+ this.fireEvent('pasteImage', {
clipboardData
});
return;
@@ -1519,12 +1524,12 @@
}
}
const types = clipboardData == null ? void 0 : clipboardData.types;
- if (!isLegacyEdge && types && (indexOf.call(types, "text/html") > -1 || !isGecko && indexOf.call(types, "text/plain") > -1 && indexOf.call(types, "text/rtf") < 0)) {
+ if (!isLegacyEdge && types && (indexOf.call(types, 'text/html') > -1 || !isGecko && indexOf.call(types, 'text/plain') > -1 && indexOf.call(types, 'text/rtf') < 0)) {
event.preventDefault();
let data;
- if (!choosePlain && (data = clipboardData.getData("text/html"))) {
+ if (!choosePlain && (data = clipboardData.getData('text/html'))) {
this.insertHTML(data, true);
- } else if ((data = clipboardData.getData("text/plain")) || (data = clipboardData.getData("text/uri-list"))) {
+ } else if ((data = clipboardData.getData('text/plain')) || (data = clipboardData.getData('text/uri-list'))) {
this.insertPlainText(data, true);
}
return;
@@ -1535,16 +1540,16 @@
const startOffset = range.startOffset;
const endContainer = range.endContainer;
const endOffset = range.endOffset;
- let pasteArea = createElement("DIV", {
- contenteditable: "true",
- style: "position:fixed; overflow:hidden; top:0; right:100%; width:1px; height:1px;"
+ let pasteArea = createElement('DIV', {
+ contenteditable: 'true',
+ style: 'position:fixed; overflow:hidden; top:0; right:100%; width:1px; height:1px;'
});
body.appendChild(pasteArea);
range.selectNodeContents(pasteArea);
this.setSelection(range);
setTimeout(() => {
try {
- let html = "";
+ let html = '';
let next = pasteArea;
let first;
while (pasteArea = next) {
@@ -1582,10 +1587,10 @@
let hasHTML = false;
while (l--) {
switch (types[l]) {
- case "text/plain":
+ case 'text/plain':
hasPlain = true;
break;
- case "text/html":
+ case 'text/html':
hasHTML = true;
break;
default:
@@ -1631,7 +1636,7 @@
fixCursor(parent);
moveRangeBoundariesDownTree(range);
}
- if (node === self._root && (node = node.firstChild) && node.nodeName === "BR") {
+ if (node === self._root && (node = node.firstChild) && node.nodeName === 'BR') {
detach(node);
}
self._ensureBottomLine();
@@ -1652,13 +1657,13 @@
detach(node);
};
var linkifyText = (self, textNode, offset) => {
- if (getNearest(textNode, self._root, "A")) {
+ if (getNearest(textNode, self._root, 'A')) {
return;
}
- const data = textNode.data || "";
+ const data = textNode.data || '';
const searchFrom = Math.max(
- data.lastIndexOf(" ", offset - 1),
- data.lastIndexOf("\xA0", offset - 1)
+ data.lastIndexOf(' ', offset - 1),
+ data.lastIndexOf('\xA0', offset - 1)
) + 1;
const searchText = data.slice(searchFrom, offset);
const match = self.linkRegExp.exec(searchText);
@@ -1676,10 +1681,10 @@
}
const defaultAttributes = self._config.tagAttributes.a;
const link = createElement(
- "A",
+ 'A',
Object.assign(
{
- href: match[1] ? /^(?:ht|f)tps?:/i.test(match[1]) ? match[1] : "http://" + match[1] : "mailto:" + match[0]
+ href: match[1] ? /^(?:ht|f)tps?:/i.test(match[1]) ? match[1] : 'http://' + match[1] : 'mailto:' + match[0]
},
defaultAttributes
)
@@ -1728,10 +1733,10 @@
}
self.setSelection(range);
} else if (current) {
- if (getNearest(current, root, "UL") || getNearest(current, root, "OL")) {
+ if (getNearest(current, root, 'UL') || getNearest(current, root, 'OL')) {
self.decreaseListLevel(range);
return;
- } else if (getNearest(current, root, "BLOCKQUOTE")) {
+ } else if (getNearest(current, root, 'BLOCKQUOTE')) {
self.removeQuote(range);
return;
}
@@ -1803,7 +1808,7 @@
cursorOffset = range.endOffset;
if (cursorContainer instanceof Element) {
nodeAfterCursor = cursorContainer.childNodes[cursorOffset];
- if (nodeAfterCursor && nodeAfterCursor.nodeName === "IMG") {
+ if (nodeAfterCursor && nodeAfterCursor.nodeName === 'IMG') {
event.preventDefault();
detach(nodeAfterCursor);
moveRangeBoundariesDownTree(range);
@@ -1826,7 +1831,7 @@
let node = getStartBlockOfRange(range, root);
let parent;
while (parent = node.parentNode) {
- if (parent.nodeName === "UL" || parent.nodeName === "OL") {
+ if (parent.nodeName === 'UL' || parent.nodeName === 'OL') {
event.preventDefault();
self.increaseListLevel(range);
break;
@@ -1840,7 +1845,7 @@
self._removeZWS();
if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) {
const node = range.startContainer;
- if (getNearest(node, root, "UL") || getNearest(node, root, "OL")) {
+ if (getNearest(node, root, 'UL') || getNearest(node, root, 'OL')) {
event.preventDefault();
self.decreaseListLevel(range);
}
@@ -1861,11 +1866,11 @@
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.") {
+ if (block && block.nodeName !== 'PRE') {
+ const text = (_a = block.textContent) == null ? void 0 : _a.trimEnd().replace(ZWS, '');
+ if (text === '*' || text === '1.') {
event.preventDefault();
- self.insertPlainText(" ", false);
+ self.insertPlainText(' ', false);
self._docWasChanged();
self.saveUndoState(range);
const walker = new TreeIterator(block, SHOW_TEXT);
@@ -1873,7 +1878,7 @@
while (textNode = walker.nextNode()) {
detach(textNode);
}
- if (text === "*") {
+ if (text === '*') {
self.makeUnorderedList();
} else {
self.makeOrderedList();
@@ -1885,7 +1890,7 @@
node = range.endContainer;
if (range.endOffset === getLength(node)) {
do {
- if (node.nodeName === "A") {
+ if (node.nodeName === 'A') {
range.setStartAfter(node);
break;
}
@@ -1909,23 +1914,27 @@
return;
}
let key = event.key;
- let modifiers = "";
- if (key !== "Backspace" && key !== "Delete") {
+ let modifiers = '';
+ const code = event.code;
+ if (/^Digit\d$/.test(code)) {
+ key = code.slice(-1);
+ }
+ if (key !== 'Backspace' && key !== 'Delete') {
if (event.altKey) {
- modifiers += "Alt-";
+ modifiers += 'Alt-';
}
if (event.ctrlKey) {
- modifiers += "Ctrl-";
+ modifiers += 'Ctrl-';
}
if (event.metaKey) {
- modifiers += "Meta-";
+ modifiers += 'Meta-';
}
if (event.shiftKey) {
- modifiers += "Shift-";
+ modifiers += 'Shift-';
}
}
- if (isWin && event.shiftKey && key === "Delete") {
- modifiers += "Shift-";
+ if (isWin && event.shiftKey && key === 'Delete') {
+ modifiers += 'Shift-';
}
key = modifiers + key;
const range = this.getSelection();
@@ -1940,25 +1949,25 @@
}
};
var keyHandlers = {
- "Backspace": Backspace,
- "Delete": Delete,
- "Tab": Tab,
- "Shift-Tab": ShiftTab,
- " ": Space,
- "ArrowLeft"(self) {
+ 'Backspace': Backspace,
+ 'Delete': Delete,
+ 'Tab': Tab,
+ 'Shift-Tab': ShiftTab,
+ ' ': Space,
+ 'ArrowLeft'(self) {
self._removeZWS();
},
- "ArrowRight"(self, event, range) {
+ 'ArrowRight'(self, event, range) {
self._removeZWS();
const root = self.getRoot();
if (rangeDoesEndAtBlockBoundary(range, root)) {
moveRangeBoundariesDownTree(range);
let node = range.endContainer;
do {
- if (node.nodeName === "CODE") {
+ if (node.nodeName === 'CODE') {
let next = node.nextSibling;
if (!(next instanceof Text)) {
- const textNode = document.createTextNode("\xA0");
+ const textNode = document.createTextNode('\xA0');
node.parentNode.insertBefore(textNode, next);
next = textNode;
}
@@ -1973,7 +1982,7 @@
};
if (!supportsInputEvents) {
keyHandlers.Enter = Enter;
- keyHandlers["Shift-Enter"] = Enter;
+ keyHandlers['Shift-Enter'] = Enter;
}
if (!isMac && !isIOS) {
keyHandlers.PageUp = (self) => {
@@ -1995,13 +2004,13 @@
}
};
};
- keyHandlers[ctrlKey + "b"] = mapKeyToFormat("B");
- keyHandlers[ctrlKey + "i"] = mapKeyToFormat("I");
- keyHandlers[ctrlKey + "u"] = mapKeyToFormat("U");
- keyHandlers[ctrlKey + "Shift-7"] = mapKeyToFormat("S");
- keyHandlers[ctrlKey + "Shift-5"] = mapKeyToFormat("SUB", { tag: "SUP" });
- keyHandlers[ctrlKey + "Shift-6"] = mapKeyToFormat("SUP", { tag: "SUB" });
- keyHandlers[ctrlKey + "Shift-8"] = (self, event) => {
+ keyHandlers[ctrlKey + 'b'] = mapKeyToFormat('B');
+ keyHandlers[ctrlKey + 'i'] = mapKeyToFormat('I');
+ keyHandlers[ctrlKey + 'u'] = mapKeyToFormat('U');
+ keyHandlers[ctrlKey + 'Shift-7'] = mapKeyToFormat('S');
+ keyHandlers[ctrlKey + 'Shift-5'] = mapKeyToFormat('SUB', { tag: 'SUP' });
+ keyHandlers[ctrlKey + 'Shift-6'] = mapKeyToFormat('SUP', { tag: 'SUB' });
+ keyHandlers[ctrlKey + 'Shift-8'] = (self, event) => {
event.preventDefault();
const path = self.getPath();
if (!/(?:^|>)UL/.test(path)) {
@@ -2010,7 +2019,7 @@
self.removeList();
}
};
- keyHandlers[ctrlKey + "Shift-9"] = (self, event) => {
+ keyHandlers[ctrlKey + 'Shift-9'] = (self, event) => {
event.preventDefault();
const path = self.getPath();
if (!/(?:^|>)OL/.test(path)) {
@@ -2019,7 +2028,7 @@
self.removeList();
}
};
- keyHandlers[ctrlKey + "["] = (self, event) => {
+ keyHandlers[ctrlKey + '['] = (self, event) => {
event.preventDefault();
const path = self.getPath();
if (/(?:^|>)BLOCKQUOTE/.test(path) || !/(?:^|>)[OU]L/.test(path)) {
@@ -2028,7 +2037,7 @@
self.decreaseListLevel();
}
};
- keyHandlers[ctrlKey + "]"] = (self, event) => {
+ keyHandlers[ctrlKey + ']'] = (self, event) => {
event.preventDefault();
const path = self.getPath();
if (/(?:^|>)BLOCKQUOTE/.test(path) || !/(?:^|>)[OU]L/.test(path)) {
@@ -2037,18 +2046,21 @@
self.increaseListLevel();
}
};
- keyHandlers[ctrlKey + "d"] = (self, event) => {
+ keyHandlers[ctrlKey + 'd'] = (self, event) => {
event.preventDefault();
self.toggleCode();
};
- keyHandlers[ctrlKey + "z"] = (self, event) => {
+ keyHandlers[ctrlKey + 'z'] = (self, event) => {
event.preventDefault();
self.undo();
};
- keyHandlers[ctrlKey + "y"] = keyHandlers[ctrlKey + "Shift-z"] = (self, event) => {
- event.preventDefault();
- self.redo();
- };
+ keyHandlers[ctrlKey + 'y'] = // Depending on platform, the Shift may cause the key to come through as
+ // upper case, but sometimes not. Just add both as shortcuts — the browser
+ // will only ever fire one or the other.
+ keyHandlers[ctrlKey + 'Shift-z'] = keyHandlers[ctrlKey + 'Shift-Z'] = (self, event) => {
+ event.preventDefault();
+ self.redo();
+ };
// source/Editor.ts
var Squire = class {
@@ -2059,15 +2071,15 @@
* editor code.
*/
this.customEvents = /* @__PURE__ */ new Set([
- "pathChange",
- "select",
- "input",
- "pasteImage",
- "undoStateChange"
+ 'pathChange',
+ 'select',
+ 'input',
+ 'pasteImage',
+ 'undoStateChange'
]);
// ---
- this.startSelectionId = "squire-selection-start";
- this.endSelectionId = "squire-selection-end";
+ this.startSelectionId = 'squire-selection-start';
+ this.endSelectionId = 'squire-selection-end';
/*
linkRegExp = new RegExp(
// Only look on boundaries
@@ -2121,10 +2133,10 @@
*/
this.linkRegExp = /\b(?:((?:(?:ht|f)tps?:\/\/|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;
this.tagAfterSplit = {
- DT: "DD",
- DD: "DT",
- LI: "LI",
- PRE: "PRE"
+ DT: 'DD',
+ DD: 'DT',
+ LI: 'LI',
+ PRE: 'PRE'
};
this._root = root;
this._config = this._makeConfig(config);
@@ -2134,7 +2146,7 @@
this._mayHaveZWS = false;
this._lastAnchorNode = null;
this._lastFocusNode = null;
- this._path = "";
+ this._path = '';
this._events = /* @__PURE__ */ new Map();
this._undoIndex = -1;
this._undoStack = [];
@@ -2142,22 +2154,22 @@
this._isInUndoState = false;
this._ignoreChange = false;
this._ignoreAllChanges = false;
- this.addEventListener("selectionchange", this._updatePathOnEvent);
- this.addEventListener("blur", this._enableRestoreSelection);
- this.addEventListener("mousedown", this._disableRestoreSelection);
- this.addEventListener("touchstart", this._disableRestoreSelection);
- this.addEventListener("focus", this._restoreSelection);
+ this.addEventListener('selectionchange', this._updatePathOnEvent);
+ this.addEventListener('blur', this._enableRestoreSelection);
+ this.addEventListener('mousedown', this._disableRestoreSelection);
+ this.addEventListener('touchstart', this._disableRestoreSelection);
+ this.addEventListener('focus', this._restoreSelection);
this._isShiftDown = false;
- this.addEventListener("cut", _onCut);
- this.addEventListener("copy", _onCopy);
- this.addEventListener("paste", _onPaste);
- this.addEventListener("drop", _onDrop);
+ this.addEventListener('cut', _onCut);
+ this.addEventListener('copy', _onCopy);
+ this.addEventListener('paste', _onPaste);
+ this.addEventListener('drop', _onDrop);
this.addEventListener(
- "keydown",
+ 'keydown',
_monitorShiftKey
);
- this.addEventListener("keyup", _monitorShiftKey);
- this.addEventListener("keydown", _onKey);
+ this.addEventListener('keyup', _monitorShiftKey);
+ this.addEventListener('keydown', _onKey);
this._keyHandlers = Object.create(keyHandlers);
const mutation = new MutationObserver(() => this._docWasChanged());
mutation.observe(root, {
@@ -2167,13 +2179,14 @@
subtree: true
});
this._mutation = mutation;
- root.setAttribute("contenteditable", "true");
+ root.setAttribute('contenteditable', 'true');
this.addEventListener(
- "beforeinput",
+ 'beforeinput',
this._beforeInput
);
- this.setHTML("");
+ this.setHTML('');
}
+
destroy() {
this._events.forEach((_, type) => {
this.removeEventListener(type);
@@ -2183,16 +2196,17 @@
this._undoStack = [];
this._undoStackLength = 0;
}
+
_makeConfig(userConfig) {
const config = {
- blockTag: "DIV",
+ blockTag: 'DIV',
blockAttributes: null,
tagAttributes: {},
classNames: {
- color: "color",
- fontFamily: "font",
- fontSize: "size",
- highlight: "highlight"
+ color: 'color',
+ fontFamily: 'font',
+ fontSize: 'size',
+ highlight: 'highlight'
},
undo: {
documentSizeThreshold: -1,
@@ -2221,113 +2235,112 @@
}
return config;
}
+
setKeyHandler(key, fn) {
this._keyHandlers[key] = fn;
return this;
}
+
_beforeInput(event) {
switch (event.inputType) {
- case "insertText":
- if (isAndroid && event.data && event.data.includes("\n")) {
- event.preventDefault();
- }
- break;
- case "insertLineBreak":
+ case 'insertLineBreak':
event.preventDefault();
this.splitBlock(true);
break;
- case "insertParagraph":
+ case 'insertParagraph':
event.preventDefault();
this.splitBlock(false);
break;
- case "insertOrderedList":
+ case 'insertOrderedList':
event.preventDefault();
this.makeOrderedList();
break;
- case "insertUnoderedList":
+ case 'insertUnoderedList':
event.preventDefault();
this.makeUnorderedList();
break;
- case "historyUndo":
+ case 'historyUndo':
event.preventDefault();
this.undo();
break;
- case "historyRedo":
+ case 'historyRedo':
event.preventDefault();
this.redo();
break;
- case "formatBold":
+ case 'formatBold':
event.preventDefault();
this.bold();
break;
- case "formaItalic":
+ case 'formaItalic':
event.preventDefault();
this.italic();
break;
- case "formatUnderline":
+ case 'formatUnderline':
event.preventDefault();
this.underline();
break;
- case "formatStrikeThrough":
+ case 'formatStrikeThrough':
event.preventDefault();
this.strikethrough();
break;
- case "formatSuperscript":
+ case 'formatSuperscript':
event.preventDefault();
this.superscript();
break;
- case "formatSubscript":
+ case 'formatSubscript':
event.preventDefault();
this.subscript();
break;
- case "formatJustifyFull":
- case "formatJustifyCenter":
- case "formatJustifyRight":
- case "formatJustifyLeft": {
+ case 'formatJustifyFull':
+ case 'formatJustifyCenter':
+ case 'formatJustifyRight':
+ case 'formatJustifyLeft': {
event.preventDefault();
let alignment = event.inputType.slice(13).toLowerCase();
- if (alignment === "full") {
- alignment = "justify";
+ if (alignment === 'full') {
+ alignment = 'justify';
}
this.setTextAlignment(alignment);
break;
}
- case "formatRemove":
+ case 'formatRemove':
event.preventDefault();
this.removeAllFormatting();
break;
- case "formatSetBlockTextDirection": {
+ case 'formatSetBlockTextDirection': {
event.preventDefault();
let dir = event.data;
- if (dir === "null") {
+ if (dir === 'null') {
dir = null;
}
this.setTextDirection(dir);
break;
}
- case "formatBackColor":
+ case 'formatBackColor':
event.preventDefault();
this.setHighlightColor(event.data);
break;
- case "formatFontColor":
+ case 'formatFontColor':
event.preventDefault();
this.setTextColor(event.data);
break;
- case "formatFontName":
+ case 'formatFontName':
event.preventDefault();
this.setFontFace(event.data);
break;
}
}
+
// --- Events
handleEvent(event) {
this.fireEvent(event.type, event);
}
+
fireEvent(type, detail) {
let handlers = this._events.get(type);
if (/^(?:focus|blur)/.test(type)) {
const isFocused = this._root === document.activeElement;
- if (type === "focus") {
+ if (type === 'focus') {
if (!isFocused || this._isFocused) {
return this;
}
@@ -2346,7 +2359,7 @@
handlers = handlers.slice();
for (const handler of handlers) {
try {
- if ("handleEvent" in handler) {
+ if ('handleEvent' in handler) {
handler.handleEvent(event);
} else {
handler.call(this, event);
@@ -2358,6 +2371,7 @@
}
return this;
}
+
addEventListener(type, fn) {
let handlers = this._events.get(type);
let target = this._root;
@@ -2365,7 +2379,7 @@
handlers = [];
this._events.set(type, handlers);
if (!this.customEvents.has(type)) {
- if (type === "selectionchange") {
+ if (type === 'selectionchange') {
target = document;
}
target.addEventListener(type, this, true);
@@ -2374,6 +2388,7 @@
handlers.push(fn);
return this;
}
+
removeEventListener(type, fn) {
const handlers = this._events.get(type);
let target = this._root;
@@ -2391,7 +2406,7 @@
if (!handlers.length) {
this._events.delete(type);
if (!this.customEvents.has(type)) {
- if (type === "selectionchange") {
+ if (type === 'selectionchange') {
target = document;
}
target.removeEventListener(type, this, true);
@@ -2400,27 +2415,33 @@
}
return this;
}
+
// --- Focus
focus() {
this._root.focus({ preventScroll: true });
return this;
}
+
blur() {
this._root.blur();
return this;
}
+
// --- Selection and bookmarking
_enableRestoreSelection() {
this._willRestoreSelection = true;
}
+
_disableRestoreSelection() {
this._willRestoreSelection = false;
}
+
_restoreSelection() {
if (this._willRestoreSelection) {
this.setSelection(this._lastSelection);
}
}
+
// ---
_removeZWS() {
if (!this._mayHaveZWS) {
@@ -2429,14 +2450,15 @@
removeZWS(this._root);
this._mayHaveZWS = false;
}
+
_saveRangeToBookmark(range) {
- let startNode = createElement("INPUT", {
+ let startNode = createElement('INPUT', {
id: this.startSelectionId,
- type: "hidden"
+ type: 'hidden'
});
- let endNode = createElement("INPUT", {
+ let endNode = createElement('INPUT', {
id: this.endSelectionId,
- type: "hidden"
+ type: 'hidden'
});
let temp;
insertNodeInRange(range, startNode);
@@ -2452,10 +2474,11 @@
range.setStartAfter(startNode);
range.setEndBefore(endNode);
}
+
_getRangeAndRemoveBookmark(range) {
const root = this._root;
- const start = root.querySelector("#" + this.startSelectionId);
- const end = root.querySelector("#" + this.endSelectionId);
+ const start = root.querySelector('#' + this.startSelectionId);
+ const end = root.querySelector('#' + this.endSelectionId);
if (start && end) {
let startContainer = start.parentNode;
let endContainer = end.parentNode;
@@ -2493,6 +2516,7 @@
}
return range || null;
}
+
getSelection() {
const selection = window.getSelection();
const root = this._root;
@@ -2521,6 +2545,7 @@
}
return range;
}
+
setSelection(range) {
this._lastSelection = range;
if (!this._isFocused) {
@@ -2528,7 +2553,7 @@
} else {
const selection = window.getSelection();
if (selection) {
- if ("setBaseAndExtent" in Selection.prototype) {
+ if ('setBaseAndExtent' in Selection.prototype) {
selection.setBaseAndExtent(
range.startContainer,
range.startOffset,
@@ -2543,6 +2568,7 @@
}
return this;
}
+
// ---
_moveCursorTo(toStart) {
const root = this._root;
@@ -2551,19 +2577,22 @@
this.setSelection(range);
return this;
}
+
moveCursorToStart() {
return this._moveCursorTo(true);
}
+
moveCursorToEnd() {
return this._moveCursorTo(false);
}
+
// ---
getCursorPosition() {
const range = this.getSelection();
let rect = range.getBoundingClientRect();
if (rect && !rect.top) {
this._ignoreChange = true;
- const node = createElement("SPAN");
+ const node = createElement('SPAN');
node.textContent = ZWS;
insertNodeInRange(range, node);
rect = node.getBoundingClientRect();
@@ -2573,15 +2602,18 @@
}
return rect;
}
+
// --- Path
getPath() {
return this._path;
}
+
_updatePathOnEvent() {
if (this._isFocused) {
this._updatePath(this.getSelection());
}
}
+
_updatePath(range, force) {
const anchor = range.startContainer;
const focus = range.endContainer;
@@ -2589,58 +2621,60 @@
if (force || anchor !== this._lastAnchorNode || focus !== this._lastFocusNode) {
this._lastAnchorNode = anchor;
this._lastFocusNode = focus;
- newPath = anchor && focus ? anchor === focus ? this._getPath(focus) : "(selection)" : "";
+ newPath = anchor && focus ? anchor === focus ? this._getPath(focus) : '(selection)' : '';
if (this._path !== newPath) {
this._path = newPath;
- this.fireEvent("pathChange", {
+ this.fireEvent('pathChange', {
path: newPath
});
}
}
- this.fireEvent(range.collapsed ? "cursor" : "select", {
+ this.fireEvent(range.collapsed ? 'cursor' : 'select', {
range
});
}
+
_getPath(node) {
const root = this._root;
const config = this._config;
- let path = "";
+ let path = '';
if (node && node !== root) {
const parent = node.parentNode;
- path = parent ? this._getPath(parent) : "";
+ path = parent ? this._getPath(parent) : '';
if (node instanceof HTMLElement) {
const id = node.id;
const classList = node.classList;
const classNames = Array.from(classList).sort();
const dir = node.dir;
const styleNames = config.classNames;
- path += (path ? ">" : "") + node.nodeName;
+ path += (path ? '>' : '') + node.nodeName;
if (id) {
- path += "#" + id;
+ path += '#' + id;
}
if (classNames.length) {
- path += ".";
- path += classNames.join(".");
+ path += '.';
+ path += classNames.join('.');
}
if (dir) {
- path += "[dir=" + dir + "]";
+ path += '[dir=' + dir + ']';
}
if (classList.contains(styleNames.highlight)) {
- path += "[backgroundColor=" + node.style.backgroundColor.replace(/ /g, "") + "]";
+ path += '[backgroundColor=' + node.style.backgroundColor.replace(/ /g, '') + ']';
}
if (classList.contains(styleNames.color)) {
- path += "[color=" + node.style.color.replace(/ /g, "") + "]";
+ path += '[color=' + node.style.color.replace(/ /g, '') + ']';
}
if (classList.contains(styleNames.fontFamily)) {
- path += "[fontFamily=" + node.style.fontFamily.replace(/ /g, "") + "]";
+ path += '[fontFamily=' + node.style.fontFamily.replace(/ /g, '') + ']';
}
if (classList.contains(styleNames.fontSize)) {
- path += "[fontSize=" + node.style.fontSize + "]";
+ path += '[fontSize=' + node.style.fontSize + ']';
}
}
}
return path;
}
+
// --- History
modifyDocument(modificationFn) {
const mutation = this._mutation;
@@ -2664,6 +2698,7 @@
}
return this;
}
+
_docWasChanged() {
resetNodeCategoryCache();
this._mayHaveZWS = true;
@@ -2676,13 +2711,14 @@
}
if (this._isInUndoState) {
this._isInUndoState = false;
- this.fireEvent("undoStateChange", {
+ this.fireEvent('undoStateChange', {
canUndo: true,
canRedo: false
});
}
- this.fireEvent("input");
+ this.fireEvent('input');
}
+
/**
* Leaves bookmark.
*/
@@ -2721,6 +2757,7 @@
}
return this;
}
+
saveUndoState(range) {
if (!range) {
range = this.getSelection();
@@ -2729,6 +2766,7 @@
this._getRangeAndRemoveBookmark(range);
return this;
}
+
undo() {
if (this._undoIndex !== 0 || !this._isInUndoState) {
this._recordUndoState(this.getSelection(), false);
@@ -2739,14 +2777,15 @@
this.setSelection(range);
}
this._isInUndoState = true;
- this.fireEvent("undoStateChange", {
+ this.fireEvent('undoStateChange', {
canUndo: this._undoIndex !== 0,
canRedo: true
});
- this.fireEvent("input");
+ this.fireEvent('input');
}
return this.focus();
}
+
redo() {
const undoIndex = this._undoIndex;
const undoStackLength = this._undoStackLength;
@@ -2757,27 +2796,30 @@
if (range) {
this.setSelection(range);
}
- this.fireEvent("undoStateChange", {
+ this.fireEvent('undoStateChange', {
canUndo: true,
canRedo: undoIndex + 2 < undoStackLength
});
- this.fireEvent("input");
+ this.fireEvent('input');
}
return this.focus();
}
+
// --- Get and set data
getRoot() {
return this._root;
}
+
_getRawHTML() {
return this._root.innerHTML;
}
+
_setRawHTML(html) {
const root = this._root;
root.innerHTML = html;
let node = root;
const child = node.firstChild;
- if (!child || child.nodeName === "BR") {
+ if (!child || child.nodeName === 'BR') {
const block = this.createDefaultBlock();
if (child) {
node.replaceChild(block, child);
@@ -2792,18 +2834,20 @@
this._ignoreChange = true;
return this;
}
+
getHTML(withBookmark) {
let range;
if (withBookmark) {
range = this.getSelection();
this._saveRangeToBookmark(range);
}
- const html = this._getRawHTML().replace(/\u200B/g, "");
+ const html = this._getRawHTML().replace(/\u200B/g, '');
if (withBookmark) {
this._getRangeAndRemoveBookmark(range);
}
return html;
}
+
setHTML(html) {
const frag = this._config.sanitizeToDOMFragment(html, this);
const root = this._root;
@@ -2812,7 +2856,7 @@
fixContainer(frag, root);
let node = frag;
let child = node.firstChild;
- if (!child || child.nodeName === "BR") {
+ if (!child || child.nodeName === 'BR') {
const block = this.createDefaultBlock();
if (child) {
node.replaceChild(block, child);
@@ -2839,6 +2883,7 @@
this._updatePath(range, true);
return this;
}
+
/**
* Insert HTML at the cursor location. If the selection is not collapsed
* insertTreeFragmentIntoRange will delete the selection so that it is
@@ -2864,20 +2909,20 @@
}
let doInsert = true;
if (isPaste) {
- const event = new CustomEvent("willPaste", {
+ const event = new CustomEvent('willPaste', {
cancelable: true,
detail: {
fragment: frag
}
});
- this.fireEvent("willPaste", event);
+ this.fireEvent('willPaste', event);
frag = event.detail.fragment;
doInsert = !event.defaultPrevented;
}
if (doInsert) {
insertTreeFragmentIntoRange(range, frag, root);
range.collapse(false);
- moveRangeBoundaryOutOf(range, "A", root);
+ moveRangeBoundaryOutOf(range, 'A', root);
this._ensureBottomLine();
}
this.setSelection(range);
@@ -2890,6 +2935,7 @@
}
return this;
}
+
insertElement(el, range) {
if (!range) {
range = this.getSelection();
@@ -2933,9 +2979,10 @@
this._updatePath(range);
return this;
}
+
insertImage(src, attributes) {
const img = createElement(
- "IMG",
+ 'IMG',
Object.assign(
{
src
@@ -2946,14 +2993,15 @@
this.insertElement(img);
return img;
}
+
insertPlainText(plainText, isPaste) {
const range = this.getSelection();
- if (range.collapsed && getNearest(range.startContainer, this._root, "PRE")) {
+ if (range.collapsed && getNearest(range.startContainer, this._root, 'PRE')) {
const startContainer = range.startContainer;
let offset = range.startOffset;
let textNode;
if (!startContainer || !(startContainer instanceof Text)) {
- const text = document.createTextNode("");
+ const text = document.createTextNode('');
startContainer.insertBefore(
text,
startContainer.childNodes[offset]
@@ -2965,13 +3013,13 @@
}
let doInsert = true;
if (isPaste) {
- const event = new CustomEvent("willPaste", {
+ const event = new CustomEvent('willPaste', {
cancelable: true,
detail: {
text: plainText
}
});
- this.fireEvent("willPaste", event);
+ this.fireEvent('willPaste', event);
plainText = event.detail.text;
doInsert = !event.defaultPrevented;
}
@@ -2983,29 +3031,31 @@
this.setSelection(range);
return this;
}
- const lines = plainText.split("\n");
+ const lines = plainText.split('\n');
const config = this._config;
const tag = config.blockTag;
const attributes = config.blockAttributes;
- const closeBlock = "" + tag + ">";
- let openBlock = "<" + tag;
+ const closeBlock = '' + tag + '>';
+ let openBlock = '<' + tag;
for (const attr in attributes) {
- openBlock += " " + attr + '="' + escapeHTML(attributes[attr]) + '"';
+ openBlock += ' ' + attr + '="' + escapeHTML(attributes[attr]) + '"';
}
- openBlock += ">";
+ openBlock += '>';
for (let i = 0, l = lines.length; i < l; i += 1) {
let line = lines[i];
- line = escapeHTML(line).replace(/ (?=(?: |$))/g, " ");
+ line = escapeHTML(line).replace(/ (?=(?: |$))/g, ' ');
if (i) {
- line = openBlock + (line || "
") + closeBlock;
+ line = openBlock + (line || '
') + closeBlock;
}
lines[i] = line;
}
- return this.insertHTML(lines.join(""), isPaste);
+ return this.insertHTML(lines.join(''), isPaste);
}
+
getSelectedText(range) {
return getTextContentsOfRange(range || this.getSelection());
}
+
// --- Inline formatting
/**
* Extracts the font-family and font-size (if any) of the element
@@ -3056,6 +3106,7 @@
}
return fontInfo;
}
+
/**
* Looks for matching tag and attributes, so won't work if
* instead of etc.
@@ -3095,6 +3146,7 @@
}
return seenNode;
}
+
changeFormat(add, remove, range, partial) {
if (!range) {
range = this.getSelection();
@@ -3119,6 +3171,7 @@
this._updatePath(range, true);
return this.focus();
}
+
_addFormat(tag, attributes, range) {
const root = this._root;
if (range.collapsed) {
@@ -3138,7 +3191,7 @@
range.commonAncestorContainer,
SHOW_ELEMENT_OR_TEXT,
(node) => {
- return (node instanceof Text || node.nodeName === "BR" || node.nodeName === "IMG") && isNodeContainedInRange(range, node, true);
+ return (node instanceof Text || node.nodeName === 'BR' || node.nodeName === 'IMG') && isNodeContainedInRange(range, node, true);
}
);
let { startContainer, startOffset, endContainer, endOffset } = range;
@@ -3183,6 +3236,7 @@
}
return range;
}
+
_removeFormat(tag, attributes, range, partial) {
this._saveRangeToBookmark(range);
let fixer;
@@ -3190,7 +3244,7 @@
if (cantFocusEmptyTextNodes) {
fixer = document.createTextNode(ZWS);
} else {
- fixer = document.createTextNode("");
+ fixer = document.createTextNode('');
}
insertNodeInRange(range, fixer);
}
@@ -3265,50 +3319,63 @@
mergeInlines(root, range);
return range;
}
+
// ---
bold() {
- return this.changeFormat({ tag: "B" });
+ return this.changeFormat({ tag: 'B' });
}
+
removeBold() {
- return this.changeFormat(null, { tag: "B" });
+ return this.changeFormat(null, { tag: 'B' });
}
+
italic() {
- return this.changeFormat({ tag: "I" });
+ return this.changeFormat({ tag: 'I' });
}
+
removeItalic() {
- return this.changeFormat(null, { tag: "I" });
+ return this.changeFormat(null, { tag: 'I' });
}
+
underline() {
- return this.changeFormat({ tag: "U" });
+ return this.changeFormat({ tag: 'U' });
}
+
removeUnderline() {
- return this.changeFormat(null, { tag: "U" });
+ return this.changeFormat(null, { tag: 'U' });
}
+
strikethrough() {
- return this.changeFormat({ tag: "S" });
+ return this.changeFormat({ tag: 'S' });
}
+
removeStrikethrough() {
- return this.changeFormat(null, { tag: "S" });
+ return this.changeFormat(null, { tag: 'S' });
}
+
subscript() {
- return this.changeFormat({ tag: "SUB" }, { tag: "SUP" });
+ return this.changeFormat({ tag: 'SUB' }, { tag: 'SUP' });
}
+
removeSubscript() {
- return this.changeFormat(null, { tag: "SUB" });
+ return this.changeFormat(null, { tag: 'SUB' });
}
+
superscript() {
- return this.changeFormat({ tag: "SUP" }, { tag: "SUB" });
+ return this.changeFormat({ tag: 'SUP' }, { tag: 'SUB' });
}
+
removeSuperscript() {
- return this.changeFormat(null, { tag: "SUP" });
+ return this.changeFormat(null, { tag: 'SUP' });
}
+
// ---
makeLink(url, attributes) {
const range = this.getSelection();
if (range.collapsed) {
- let protocolEnd = url.indexOf(":") + 1;
+ let protocolEnd = url.indexOf(':') + 1;
if (protocolEnd) {
- while (url[protocolEnd] === "/") {
+ while (url[protocolEnd] === '/') {
protocolEnd += 1;
}
}
@@ -3326,30 +3393,32 @@
);
return this.changeFormat(
{
- tag: "A",
+ tag: 'A',
attributes
},
{
- tag: "A"
+ tag: 'A'
},
range
);
}
+
removeLink() {
return this.changeFormat(
null,
{
- tag: "A"
+ tag: 'A'
},
this.getSelection(),
true
);
}
+
addDetectedLinks(searchInNode, root) {
const walker = new TreeIterator(
searchInNode,
SHOW_TEXT,
- (node2) => !getNearest(node2, root || this._root, "A")
+ (node2) => !getNearest(node2, root || this._root, 'A')
);
const linkRegExp = this.linkRegExp;
const defaultAttributes = this._config.tagAttributes.a;
@@ -3368,10 +3437,10 @@
);
}
const child = createElement(
- "A",
+ 'A',
Object.assign(
{
- href: match[1] ? /^(?:ht|f)tps?:/i.test(match[1]) ? match[1] : "http://" + match[1] : "mailto:" + match[0]
+ href: match[1] ? /^(?:ht|f)tps?:/i.test(match[1]) ? match[1] : 'http://' + match[1] : 'mailto:' + match[0]
},
defaultAttributes
)
@@ -3383,71 +3452,76 @@
}
return this;
}
+
// ---
setFontFace(name) {
const className = this._config.classNames.fontFamily;
return this.changeFormat(
name ? {
- tag: "SPAN",
+ tag: 'SPAN',
attributes: {
class: className,
- style: "font-family: " + name + ", sans-serif;"
+ style: 'font-family: ' + name + ', sans-serif;'
}
} : null,
{
- tag: "SPAN",
+ tag: 'SPAN',
attributes: { class: className }
}
);
}
+
setFontSize(size) {
const className = this._config.classNames.fontSize;
return this.changeFormat(
size ? {
- tag: "SPAN",
+ tag: 'SPAN',
attributes: {
class: className,
- style: "font-size: " + (typeof size === "number" ? size + "px" : size)
+ style: 'font-size: ' + (typeof size === 'number' ? size + 'px' : size)
}
} : null,
{
- tag: "SPAN",
+ tag: 'SPAN',
attributes: { class: className }
}
);
}
+
setTextColor(color) {
const className = this._config.classNames.color;
return this.changeFormat(
color ? {
- tag: "SPAN",
+ tag: 'SPAN',
attributes: {
class: className,
- style: "color:" + color
+ style: 'color:' + color
}
} : null,
{
- tag: "SPAN",
+ tag: 'SPAN',
attributes: { class: className }
}
);
}
+
setHighlightColor(color) {
const className = this._config.classNames.highlight;
return this.changeFormat(
color ? {
- tag: "SPAN",
+ tag: 'SPAN',
attributes: {
class: className,
- style: "background-color:" + color
+ style: 'background-color:' + color
}
} : null,
{
- tag: "SPAN",
+ tag: 'SPAN',
attributes: { class: className }
}
);
}
+
// --- Block formatting
_ensureBottomLine() {
const root = this._root;
@@ -3456,12 +3530,14 @@
root.appendChild(this.createDefaultBlock());
}
}
+
createDefaultBlock(children) {
const config = this._config;
return fixCursor(
createElement(config.blockTag, config.blockAttributes, children)
);
}
+
splitBlock(lineBreakOnly, range) {
if (!range) {
range = this.getSelection();
@@ -3486,15 +3562,15 @@
}, 0);
}
block = getStartBlockOfRange(range, root);
- if (block && (parent = getNearest(block, root, "PRE"))) {
+ if (block && (parent = getNearest(block, root, 'PRE'))) {
moveRangeBoundariesDownTree(range);
node = range.startContainer;
const offset2 = range.startOffset;
if (!(node instanceof Text)) {
- node = document.createTextNode("");
+ node = document.createTextNode('');
parent.insertBefore(node, parent.firstChild);
}
- 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);
nodeAfterSplit = split(
node,
@@ -3513,7 +3589,7 @@
}
range.setStart(node, 0);
} else {
- node.insertData(offset2, "\n");
+ node.insertData(offset2, '\n');
fixCursor(parent);
if (node.length === offset2 + 1) {
range.setStartAfter(node);
@@ -3528,21 +3604,21 @@
return this;
}
if (!block || lineBreakOnly || /^T[HD]$/.test(block.nodeName)) {
- moveRangeBoundaryOutOf(range, "A", root);
- insertNodeInRange(range, createElement("BR"));
+ moveRangeBoundaryOutOf(range, 'A', root);
+ insertNodeInRange(range, createElement('BR'));
range.collapse(false);
this.setSelection(range);
this._updatePath(range, true);
return this;
}
- if (parent = getNearest(block, root, "LI")) {
+ if (parent = getNearest(block, root, 'LI')) {
block = parent;
}
if (isEmptyBlock(block)) {
- if (getNearest(block, root, "UL") || getNearest(block, root, "OL")) {
+ if (getNearest(block, root, 'UL') || getNearest(block, root, 'OL')) {
this.decreaseListLevel(range);
return this;
- } else if (getNearest(block, root, "BLOCKQUOTE")) {
+ } else if (getNearest(block, root, 'BLOCKQUOTE')) {
this.removeQuote(range);
return this;
}
@@ -3577,21 +3653,21 @@
while (nodeAfterSplit instanceof Element) {
let child = nodeAfterSplit.firstChild;
let next;
- if (nodeAfterSplit.nodeName === "A" && (!nodeAfterSplit.textContent || nodeAfterSplit.textContent === ZWS)) {
- child = document.createTextNode("");
+ if (nodeAfterSplit.nodeName === 'A' && (!nodeAfterSplit.textContent || nodeAfterSplit.textContent === ZWS)) {
+ child = document.createTextNode('');
replaceWith(nodeAfterSplit, child);
nodeAfterSplit = child;
break;
}
while (child && child instanceof Text && !child.data) {
next = child.nextSibling;
- if (!next || next.nodeName === "BR") {
+ if (!next || next.nodeName === 'BR') {
break;
}
detach(child);
child = next;
}
- if (!child || child.nodeName === "BR" || child instanceof Text) {
+ if (!child || child.nodeName === 'BR' || child instanceof Text) {
break;
}
nodeAfterSplit = child;
@@ -3601,6 +3677,7 @@
this._updatePath(range, true);
return this;
}
+
forEachBlock(fn, mutates, range) {
if (!range) {
range = this.getSelection();
@@ -3624,6 +3701,7 @@
}
return this;
}
+
modifyBlocks(modify, range) {
if (!range) {
range = this.getSelection();
@@ -3661,32 +3739,35 @@
this._updatePath(range, true);
return this;
}
+
// ---
setTextAlignment(alignment) {
this.forEachBlock((block) => {
const className = block.className.split(/\s+/).filter((klass) => {
return !!klass && !/^align/.test(klass);
- }).join(" ");
+ }).join(' ');
if (alignment) {
- block.className = className + " align-" + alignment;
+ block.className = className + ' align-' + alignment;
block.style.textAlign = alignment;
} else {
block.className = className;
- block.style.textAlign = "";
+ block.style.textAlign = '';
}
}, true);
return this.focus();
}
+
setTextDirection(direction) {
this.forEachBlock((block) => {
if (direction) {
block.dir = direction;
} else {
- block.removeAttribute("dir");
+ block.removeAttribute('dir');
}
}, true);
return this.focus();
}
+
// ---
_getListSelection(range, root) {
let list = range.commonAncestorContainer;
@@ -3712,6 +3793,7 @@
}
return [list, startLi, endLi];
}
+
increaseListLevel(range) {
if (!range) {
range = this.getSelection();
@@ -3748,6 +3830,7 @@
this._updatePath(range, true);
return this.focus();
}
+
decreaseListLevel(range) {
if (!range) {
range = this.getSelection();
@@ -3770,7 +3853,7 @@
if (startLi) {
let newParent = list.parentNode;
insertBefore = !endLi.nextSibling ? list.nextSibling : split(list, endLi.nextSibling, newParent, root);
- if (newParent !== root && newParent.nodeName === "LI") {
+ if (newParent !== root && newParent.nodeName === 'LI') {
newParent = newParent.parentNode;
while (insertBefore) {
next = insertBefore.nextSibling;
@@ -3783,7 +3866,7 @@
do {
next = startLi === endLi ? null : startLi.nextSibling;
list.removeChild(startLi);
- if (makeNotList && startLi.nodeName === "LI") {
+ if (makeNotList && startLi.nodeName === 'LI') {
startLi = this.createDefaultBlock([empty(startLi)]);
}
newParent.insertBefore(startLi, insertBefore);
@@ -3800,6 +3883,7 @@
this._updatePath(range, true);
return this.focus();
}
+
_makeList(frag, type) {
const walker = getBlockWalker(frag, this._root);
const tagAttributes = this._config.tagAttributes;
@@ -3812,7 +3896,7 @@
walker.currentNode = node.lastChild;
}
if (!(node instanceof HTMLLIElement)) {
- const newLi = createElement("LI", listItemAttrs);
+ const newLi = createElement('LI', listItemAttrs);
if (node.dir) {
newLi.dir = node.dir;
}
@@ -3838,18 +3922,21 @@
}
return frag;
}
+
makeUnorderedList() {
- this.modifyBlocks((frag) => this._makeList(frag, "UL"));
+ this.modifyBlocks((frag) => this._makeList(frag, 'UL'));
return this.focus();
}
+
makeOrderedList() {
- this.modifyBlocks((frag) => this._makeList(frag, "OL"));
+ this.modifyBlocks((frag) => this._makeList(frag, 'OL'));
return this.focus();
}
+
removeList() {
this.modifyBlocks((frag) => {
- const lists = frag.querySelectorAll("UL, OL");
- const items = frag.querySelectorAll("LI");
+ const lists = frag.querySelectorAll('UL, OL');
+ const items = frag.querySelectorAll('LI');
const root = this._root;
for (let i = 0, l = lists.length; i < l; i += 1) {
const list = lists[i];
@@ -3870,11 +3957,12 @@
});
return this.focus();
}
+
// ---
increaseQuoteLevel(range) {
this.modifyBlocks(
(frag) => createElement(
- "BLOCKQUOTE",
+ 'BLOCKQUOTE',
this._config.tagAttributes.blockquote,
[frag]
),
@@ -3882,10 +3970,11 @@
);
return this.focus();
}
+
decreaseQuoteLevel(range) {
this.modifyBlocks((frag) => {
- Array.from(frag.querySelectorAll("blockquote")).filter((el) => {
- return !getNearest(el.parentNode, frag, "BLOCKQUOTE");
+ Array.from(frag.querySelectorAll('blockquote')).filter((el) => {
+ return !getNearest(el.parentNode, frag, 'BLOCKQUOTE');
}).forEach((el) => {
replaceWith(el, empty(el));
});
@@ -3893,22 +3982,24 @@
}, range);
return this.focus();
}
+
removeQuote(range) {
this.modifyBlocks(
() => this.createDefaultBlock([
- createElement("INPUT", {
+ createElement('INPUT', {
id: this.startSelectionId,
- type: "hidden"
+ type: 'hidden'
}),
- createElement("INPUT", {
+ createElement('INPUT', {
id: this.endSelectionId,
- type: "hidden"
+ type: 'hidden'
})
]),
range
);
return this.focus();
}
+
// ---
code() {
const range = this.getSelection();
@@ -3919,7 +4010,7 @@
const blockWalker = getBlockWalker(frag, root);
let node;
while (node = blockWalker.nextNode()) {
- let nodes = node.querySelectorAll("BR");
+ let nodes = node.querySelectorAll('BR');
const brBreaksLine = [];
let l = nodes.length;
for (let i = 0; i < l; i += 1) {
@@ -3930,26 +4021,26 @@
if (!brBreaksLine[l]) {
detach(br);
} else {
- replaceWith(br, document.createTextNode("\n"));
+ replaceWith(br, document.createTextNode('\n'));
}
}
- nodes = node.querySelectorAll("CODE");
+ nodes = node.querySelectorAll('CODE');
l = nodes.length;
while (l--) {
replaceWith(nodes[l], empty(nodes[l]));
}
if (output.childNodes.length) {
- output.appendChild(document.createTextNode("\n"));
+ output.appendChild(document.createTextNode('\n'));
}
output.appendChild(empty(node));
}
const textWalker = new TreeIterator(output, SHOW_TEXT);
while (node = textWalker.nextNode()) {
- node.data = node.data.replace(/ /g, " ");
+ node.data = node.data.replace(/ /g, ' ');
}
output.normalize();
return fixCursor(
- createElement("PRE", this._config.tagAttributes.pre, [
+ createElement('PRE', this._config.tagAttributes.pre, [
output
])
);
@@ -3958,7 +4049,7 @@
} else {
this.changeFormat(
{
- tag: "CODE",
+ tag: 'CODE',
attributes: this._config.tagAttributes.code
},
null,
@@ -3967,14 +4058,15 @@
}
return this;
}
+
removeCode() {
const range = this.getSelection();
const ancestor = range.commonAncestorContainer;
- const inPre = getNearest(ancestor, this._root, "PRE");
+ const inPre = getNearest(ancestor, this._root, 'PRE');
if (inPre) {
this.modifyBlocks((frag) => {
const root = this._root;
- const pres = frag.querySelectorAll("PRE");
+ const pres = frag.querySelectorAll('PRE');
let l = pres.length;
while (l--) {
const pre = pres[l];
@@ -3982,14 +4074,14 @@
let node;
while (node = walker.nextNode()) {
let value = node.data;
- value = value.replace(/ (?= )/g, "\xA0");
+ value = value.replace(/ (?= )/g, '\xA0');
const contents = document.createDocumentFragment();
let index;
- while ((index = value.indexOf("\n")) > -1) {
+ while ((index = value.indexOf('\n')) > -1) {
contents.appendChild(
document.createTextNode(value.slice(0, index))
);
- contents.appendChild(createElement("BR"));
+ contents.appendChild(createElement('BR'));
value = value.slice(index + 1);
}
node.parentNode.insertBefore(contents, node);
@@ -4002,24 +4094,26 @@
}, range);
this.focus();
} else {
- this.changeFormat(null, { tag: "CODE" }, range);
+ this.changeFormat(null, { tag: 'CODE' }, range);
}
return this;
}
+
toggleCode() {
- if (this.hasFormat("PRE") || this.hasFormat("CODE")) {
+ if (this.hasFormat('PRE') || this.hasFormat('CODE')) {
this.removeCode();
} else {
this.code();
}
return this;
}
+
// ---
_removeFormatting(root, clean) {
for (let node = root.firstChild, next; node; node = next) {
next = node.nextSibling;
if (isInline(node)) {
- if (node instanceof Text || node.nodeName === "BR" || node.nodeName === "IMG") {
+ if (node instanceof Text || node.nodeName === 'BR' || node.nodeName === 'IMG') {
clean.appendChild(node);
continue;
}
@@ -4038,6 +4132,7 @@
}
return clean;
}
+
removeAllFormatting(range) {
if (!range) {
range = this.getSelection();