Squire use double quotes as in original source

This commit is contained in:
the-djmaze 2023-12-10 17:04:01 +01:00
parent c61f4a6918
commit 4bb1e24222

View file

@ -82,7 +82,7 @@ const
return !isLeaf(node) && ( return !isLeaf(node) && (
node.nodeType === node2.nodeType && node.nodeType === node2.nodeType &&
node.nodeName === node2.nodeName && node.nodeName === node2.nodeName &&
node.nodeName !== 'A' && node.nodeName !== "A" &&
node.className === node2.className && node.className === node2.className &&
node.style?.cssText === node2.style?.cssText node.style?.cssText === node2.style?.cssText
); );
@ -333,14 +333,14 @@ const
[...container.childNodes].forEach(child => { [...container.childNodes].forEach(child => {
isBR = child.nodeName === "BR"; isBR = child.nodeName === "BR";
if (!isBR && child.parentNode == root && isInline(child) if (!isBR && child.parentNode == root && isInline(child)
// && (blockTag !== 'DIV' || (child.matches && !child.matches(phrasingElements))) // && (blockTag !== "DIV" || (child.matches && !child.matches(phrasingElements)))
) { ) {
wrapper = wrapper || createElement("DIV"); wrapper = wrapper || createElement("DIV");
wrapper.append(child); wrapper.append(child);
} else if (isBR || wrapper) { } else if (isBR || wrapper) {
wrapper = wrapper || createElement("DIV"); wrapper = wrapper || createElement("DIV");
fixCursor(wrapper); fixCursor(wrapper);
child[isBR ? 'replaceWith' : 'before'](wrapper); child[isBR ? "replaceWith" : "before"](wrapper);
wrapper = null; wrapper = null;
} }
isContainer(child) && fixContainer(child, root); isContainer(child) && fixContainer(child, root);
@ -495,32 +495,32 @@ const
styleToSemantic = { styleToSemantic = {
fontWeight: { fontWeight: {
regexp: /^bold|^700/i, regexp: /^bold|^700/i,
replace: () => createElement('B') replace: () => createElement("B")
}, },
fontStyle: { fontStyle: {
regexp: /^italic/i, regexp: /^italic/i,
replace: () => createElement('I') replace: () => createElement("I")
}, },
fontFamily: { fontFamily: {
regexp: notWS, regexp: notWS,
replace: (doc, family) => createElement('SPAN', { replace: (doc, family) => createElement("SPAN", {
style: 'font-family:' + family style: 'font-family:' + family
}) })
}, },
fontSize: { fontSize: {
regexp: notWS, regexp: notWS,
replace: (doc, size) => createElement('SPAN', { replace: (doc, size) => createElement("SPAN", {
style: 'font-size:' + size style: 'font-size:' + size
}) })
}, },
textDecoration: { textDecoration: {
regexp: /^underline/i, regexp: /^underline/i,
replace: () => createElement('U') replace: () => createElement("U")
} }
/* /*
textDecoration: { textDecoration: {
regexp: /^line-through/i, regexp: /^line-through/i,
replace: doc => createElement('S') replace: doc => createElement("S")
} }
*/ */
}, },
@ -563,14 +563,14 @@ const
}, },
fontSizes = { fontSizes = {
1: 'x-small', 1: 'x-small',
2: 'small', 2: "small",
3: 'medium', 3: "medium",
4: 'large', 4: "large",
5: 'x-large', 5: 'x-large',
6: 'xx-large', 6: 'xx-large',
7: 'xxx-large', 7: 'xxx-large',
'-1': 'smaller', '-1': "smaller",
'+1': 'larger' '+1': "larger"
}, },
stylesRewriters = { stylesRewriters = {
STRONG: replaceWithTag("B"), STRONG: replaceWithTag("B"),
@ -582,7 +582,7 @@ const
let face = node.face, let face = node.face,
size = node.size, size = node.size,
color = node.color, color = node.color,
newTag = createElement('SPAN'), newTag = createElement("SPAN"),
css = newTag.style; css = newTag.style;
newTag.style.cssText = node.style.cssText; newTag.style.cssText = node.style.cssText;
if (face) { if (face) {
@ -606,7 +606,7 @@ const
// CODE: // CODE:
// SAMP: // SAMP:
TT: node => { TT: node => {
let el = createElement('SPAN', { let el = createElement("SPAN", {
style: 'font-family:menlo,consolas,"courier new",monospace' style: 'font-family:menlo,consolas,"courier new",monospace'
}); });
node.replaceWith(el); node.replaceWith(el);
@ -650,7 +650,7 @@ const
continue; continue;
} }
if (childLength) { if (childLength) {
cleanTree(child, preserveWS || (nodeName === 'PRE')); cleanTree(child, preserveWS || (nodeName === "PRE"));
} }
/* /*
} else { } else {
@ -667,7 +667,7 @@ const
walker.currentNode = child; walker.currentNode = child;
while (sibling = walker.previousPONode()) { while (sibling = walker.previousPONode()) {
nodeName = sibling.nodeName; nodeName = sibling.nodeName;
if (nodeName === 'IMG' || (nodeName === '#text' && notWS.test(sibling.data))) { if (nodeName === "IMG" || (nodeName === '#text' && notWS.test(sibling.data))) {
break; break;
} }
if (!isInline(sibling)) { if (!isInline(sibling)) {
@ -680,7 +680,7 @@ const
if (endsWithWS) { if (endsWithWS) {
walker.currentNode = child; walker.currentNode = child;
while (sibling = walker.nextNode()) { while (sibling = walker.nextNode()) {
if (nodeName === 'IMG' || (nodeName === '#text' && notWS.test(sibling.data))) { if (nodeName === "IMG" || (nodeName === '#text' && notWS.test(sibling.data))) {
break; break;
} }
if (!isInline(sibling)) { if (!isInline(sibling)) {
@ -724,7 +724,7 @@ const
// elements at the end of each block will then have them added back in a later // elements at the end of each block will then have them added back in a later
// fixCursor method call. // fixCursor method call.
cleanupBRs = (node, root, keepForBlankLine) => { cleanupBRs = (node, root, keepForBlankLine) => {
const brs = node.querySelectorAll('BR'); const brs = node.querySelectorAll("BR");
let l = brs.length; let l = brs.length;
let br, parent; let br, parent;
while (l--) { while (l--) {
@ -1081,7 +1081,7 @@ const
range.collapse(); // collapse to end range.collapse(); // collapse to end
// Where will we split up to? First blockquote parent, otherwise root. // Where will we split up to? First blockquote parent, otherwise root.
stopPoint = getClosest(range.endContainer, root, 'BLOCKQUOTE') || root; stopPoint = getClosest(range.endContainer, root, "BLOCKQUOTE") || root;
// Merge the contents of the first block in the frag with the focused block. // Merge the contents of the first block in the frag with the focused block.
// If there are contents in the block after the focus point, collect this // If there are contents in the block after the focus point, collect this
@ -1269,7 +1269,7 @@ const
setClipboardData = (event, contents, root) => { setClipboardData = (event, contents, root) => {
let clipboardData = event.clipboardData; let clipboardData = event.clipboardData;
let body = doc.body; let body = doc.body;
let node = createElement('div'); let node = createElement("div");
let html, text; let html, text;
node.append(contents); node.append(contents);
@ -1280,7 +1280,7 @@ const
// calculating innerText, even though they don't actually affect // calculating innerText, even though they don't actually affect
// display, so we need to remove them first. // display, so we need to remove them first.
cleanupBRs(node, root, true); cleanupBRs(node, root, true);
node.setAttribute('style', node.setAttribute("style",
'position:fixed;overflow:hidden;bottom:100%;right:100%;'); 'position:fixed;overflow:hidden;bottom:100%;right:100%;');
body.append(node); body.append(node);
text = (node.innerText || node.textContent).replace(NBSP, ' '); // Replace nbsp with regular space text = (node.innerText || node.textContent).replace(NBSP, ' '); // Replace nbsp with regular space
@ -1371,7 +1371,7 @@ const
// plain text. // plain text.
} else if (type === 'text/plain' || type === 'text/uri-list') { } else if (type === 'text/plain' || type === 'text/uri-list') {
plainItem = item; plainItem = item;
} else if (item.kind === 'file' && /^image\/(png|jpeg|webp)/.test(type)) { } else if (item.kind === "file" && /^image\/(png|jpeg|webp)/.test(type)) {
imageItem = item; imageItem = item;
} }
}); });
@ -1380,8 +1380,8 @@ const
if (imageItem) { if (imageItem) {
let reader = new FileReader(); let reader = new FileReader();
reader.onload = event => { reader.onload = event => {
let img = createElement('img', {src: event.target.result}), let img = createElement("img", {src: event.target.result}),
canvas = createElement('canvas'), canvas = createElement("canvas"),
ctx = canvas.getContext('2d'); ctx = canvas.getContext('2d');
img.onload = ()=>{ img.onload = ()=>{
ctx.drawImage(img, 0, 0); ctx.drawImage(img, 0, 0);
@ -1452,7 +1452,7 @@ const
// it removes the div and replaces it with just a <br> inside the // it removes the div and replaces it with just a <br> inside the
// root. Detach the <br>; the _ensureBottomLine call will insert a new // root. Detach the <br>; the _ensureBottomLine call will insert a new
// block. // block.
if (node === self._root && (node = node.firstChild) && node.nodeName === 'BR') { if (node === self._root && (node = node.firstChild) && node.nodeName === "BR") {
detach(node); detach(node);
} }
self._ensureBottomLine(); self._ensureBottomLine();
@ -1511,7 +1511,7 @@ const
else if (current) { else if (current) {
let parent = getClosest(current, root, 'UL,OL,BLOCKQUOTE'); let parent = getClosest(current, root, 'UL,OL,BLOCKQUOTE');
if (parent) { if (parent) {
return ('BLOCKQUOTE' === parent.nodeName) return ("BLOCKQUOTE" === parent.nodeName)
// Break blockquote // Break blockquote
? self.decreaseQuoteLevel(range) ? self.decreaseQuoteLevel(range)
// Break list // Break list
@ -1606,7 +1606,7 @@ const
// We need to apply the backspace/delete handlers regardless of control key modifiers. // We need to apply the backspace/delete handlers regardless of control key modifiers.
// ctrl-shift-key or meta-shift-key // ctrl-shift-key or meta-shift-key
if (key !== 'backspace' && key !== 'delete') { if (key !== "backspace" && key !== "delete") {
if (event.shiftKey) { key = 'shift-' + key; } if (event.shiftKey) { key = 'shift-' + key; }
if (event[osKey]) { key = ctrlKey + key; } if (event[osKey]) { key = ctrlKey + key; }
// if (event.altKey) { key = 'alt-' + key; } // if (event.altKey) { key = 'alt-' + key; }
@ -1637,7 +1637,7 @@ const
range = createRange(root.insertBefore( range = createRange(root.insertBefore(
this.createDefaultBlock(), nextElement this.createDefaultBlock(), nextElement
), 0); ), 0);
if (nextElement.tagName === 'BR') { if (nextElement.tagName === "BR") {
// delete it because a new <br> is created by createDefaultBlock() // delete it because a new <br> is created by createDefaultBlock()
root.removeChild(nextElement); root.removeChild(nextElement);
} }
@ -1658,9 +1658,9 @@ const
if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) { if (range.collapsed && rangeDoesStartAtBlockBoundary(range, root)) {
node = getStartBlockOfRange(range, root); node = getStartBlockOfRange(range, root);
// Iterate through the block's parents // Iterate through the block's parents
while ((parent = node.parentNode)) { while (parent = node.parentNode) {
// If we find a UL or OL (so are in a list, node must be an LI) // If we find a UL or OL (so are in a list, node must be an LI)
if (parent.nodeName === 'UL' || parent.nodeName === 'OL') { if (parent.nodeName === "UL" || parent.nodeName === "OL") {
// Then increase the list level // Then increase the list level
event.preventDefault(); event.preventDefault();
self.increaseListLevel(range); self.increaseListLevel(range);
@ -1697,7 +1697,7 @@ const
let node = range.endContainer; let node = range.endContainer;
if (range.collapsed && range.endOffset === getLength(node)) { if (range.collapsed && range.endOffset === getLength(node)) {
do { do {
if (node.nodeName === 'A') { if (node.nodeName === "A") {
range.setStartAfter(node); range.setStartAfter(node);
break; break;
} }
@ -1725,14 +1725,14 @@ const
self[method](); self[method]();
}, },
blockTag = 'DIV', blockTag = "DIV",
DOCUMENT_POSITION_PRECEDING = 2, // Node.DOCUMENT_POSITION_PRECEDING DOCUMENT_POSITION_PRECEDING = 2, // Node.DOCUMENT_POSITION_PRECEDING
NBSP = '\u00A0', NBSP = '\u00A0',
win = doc.defaultView, win = doc.defaultView,
osKey = isMac ? 'metaKey' : 'ctrlKey', osKey = isMac ? "metaKey" : "ctrlKey",
filterAccept = NodeFilter.FILTER_ACCEPT, filterAccept = NodeFilter.FILTER_ACCEPT,
/* /*
@ -1765,7 +1765,7 @@ const
setAttributes = (node, props) => { setAttributes = (node, props) => {
props && Object.entries(props).forEach(([k,v]) => { props && Object.entries(props).forEach(([k,v]) => {
if ('style' === k && typeof v === 'object') { if ("style" === k && typeof v === "object") {
Object.entries(v).forEach(([k,v]) => node.style[k] = v); Object.entries(v).forEach(([k,v]) => node.style[k] = v);
} else if (v != null) { } else if (v != null) {
node.setAttribute(k, v); node.setAttribute(k, v);
@ -1777,14 +1777,14 @@ const
newContentWalker = root => createTreeWalker(root, newContentWalker = root => createTreeWalker(root,
SHOW_ELEMENT_OR_TEXT, SHOW_ELEMENT_OR_TEXT,
node => isTextNode(node) ? notWS.test(node.data) : node.nodeName === 'IMG' node => isTextNode(node) ? notWS.test(node.data) : node.nodeName === "IMG"
), ),
didError = error => console.error(error), didError = error => console.error(error),
detachUneditableNode = (node, root) => { detachUneditableNode = (node, root) => {
let parent; let parent;
while ((parent = node.parentNode)) { while (parent = node.parentNode) {
if (parent === root || parent.isContentEditable) { if (parent === root || parent.isContentEditable) {
break; break;
} }
@ -1868,23 +1868,23 @@ const
endSelectionId = 'squire-selection-end', endSelectionId = 'squire-selection-end',
createBookmarkNodes = () => [ createBookmarkNodes = () => [
createElement('INPUT', { createElement("INPUT", {
id: startSelectionId, id: startSelectionId,
type: 'hidden' type: "hidden"
}), }),
createElement('INPUT', { createElement("INPUT", {
id: endSelectionId, id: endSelectionId,
type: 'hidden' type: "hidden"
}) })
], ],
// --- Block formatting --- // --- Block formatting ---
tagAfterSplit = { tagAfterSplit = {
DT: 'DD', DT: "DD",
DD: 'DT', DD: "DT",
LI: 'LI', LI: "LI",
PRE: 'PRE' PRE: "PRE"
}, },
getListSelection = (range, root) => { getListSelection = (range, root) => {
@ -1918,12 +1918,12 @@ const
node, tag, prev, newLi; node, tag, prev, newLi;
while (node = walker.nextNode()) { while (node = walker.nextNode()) {
if (node.parentNode.nodeName === 'LI') { if (node.parentNode.nodeName === "LI") {
node = node.parentNode; node = node.parentNode;
walker.currentNode = node.lastChild; walker.currentNode = node.lastChild;
} }
if (node.nodeName !== 'LI') { if (node.nodeName !== "LI") {
newLi = createElement('LI'); newLi = createElement("LI");
if (node.dir) { if (node.dir) {
newLi.dir = node.dir; newLi.dir = node.dir;
} }
@ -1976,19 +1976,19 @@ const
linkRegExp = /\b(?:((https?:\/\/)?(?:www\d{0,3}\.|[a-z0-9][a-z0-9.-]*\.[a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:[^\s?&`!()[\]{};:'".,<>«»“”‘’]|\([^\s()<>]+\)))|([\w\-.%+]+@(?:[\w-]+\.)+[a-z]{2,}\b(?:\?[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+(?:&[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+)*)?))/i, linkRegExp = /\b(?:((https?:\/\/)?(?:www\d{0,3}\.|[a-z0-9][a-z0-9.-]*\.[a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:[^\s?&`!()[\]{};:'".,<>«»“”‘’]|\([^\s()<>]+\)))|([\w\-.%+]+@(?:[\w-]+\.)+[a-z]{2,}\b(?:\?[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+(?:&[^&?\s]+=[^\s?&`!()[\]{};:'".,<>«»“”‘’]+)*)?))/i,
addLinks = (frag, root) => { addLinks = (frag, root) => {
let walker = createTreeWalker(frag, SHOW_TEXT, node => !getClosest(node, root, 'A')); let walker = createTreeWalker(frag, SHOW_TEXT, node => !getClosest(node, root, "A"));
let node, data, parent, match, index, endIndex, child; let node, data, parent, match, index, endIndex, child;
while ((node = walker.nextNode())) { while (node = walker.nextNode()) {
data = node.data; data = node.data;
parent = node.parentNode; parent = node.parentNode;
while ((match = linkRegExp.exec(data))) { while (match = linkRegExp.exec(data)) {
index = match.index; index = match.index;
endIndex = index + match[0].length; endIndex = index + match[0].length;
if (index) { if (index) {
child = doc.createTextNode(data.slice(0, index)); child = doc.createTextNode(data.slice(0, index));
parent.insertBefore(child, node); parent.insertBefore(child, node);
} }
child = createElement('A', { child = createElement("A", {
href: match[1] href: match[1]
? (match[2] ? match[1] : 'https://' + match[1]) ? (match[2] ? match[1] : 'https://' + match[1])
: 'mailto:' + match[0] : 'mailto:' + match[0]
@ -2009,22 +2009,22 @@ if (!isMac && !isIOS) {
keyHandlers.pagedown = self => self._moveCursorTo(false); keyHandlers.pagedown = self => self._moveCursorTo(false);
} }
keyHandlers[ctrlKey + 'b'] = mapKeyToFormat('B'); keyHandlers[ctrlKey + "b"] = mapKeyToFormat("B");
keyHandlers[ctrlKey + 'i'] = mapKeyToFormat('I'); keyHandlers[ctrlKey + "i"] = mapKeyToFormat("I");
keyHandlers[ctrlKey + 'u'] = mapKeyToFormat('U'); keyHandlers[ctrlKey + "u"] = mapKeyToFormat("U");
keyHandlers[ctrlKey + 'shift-7'] = mapKeyToFormat('S'); keyHandlers[ctrlKey + 'shift-7'] = mapKeyToFormat("S");
keyHandlers[ctrlKey + 'shift-5'] = mapKeyToFormat('SUB', 'SUP'); keyHandlers[ctrlKey + 'shift-5'] = mapKeyToFormat("SUB", "SUP");
keyHandlers[ctrlKey + 'shift-6'] = mapKeyToFormat('SUP', 'SUB'); keyHandlers[ctrlKey + 'shift-6'] = mapKeyToFormat("SUP", "SUB");
keyHandlers[ctrlKey + 'shift-8'] = toggleList('UL', 'makeUnorderedList'); keyHandlers[ctrlKey + 'shift-8'] = toggleList("UL", "makeUnorderedList");
keyHandlers[ctrlKey + 'shift-9'] = toggleList('OL', 'makeOrderedList'); keyHandlers[ctrlKey + 'shift-9'] = toggleList("OL", "makeOrderedList");
keyHandlers[ctrlKey + '['] = changeIndentationLevel('decrease'); keyHandlers[ctrlKey + '['] = changeIndentationLevel("decrease");
keyHandlers[ctrlKey + ']'] = changeIndentationLevel('increase'); keyHandlers[ctrlKey + ']'] = changeIndentationLevel("increase");
keyHandlers[ctrlKey + 'd'] = mapKeyTo('toggleCode'); keyHandlers[ctrlKey + "d"] = mapKeyTo("toggleCode");
keyHandlers[ctrlKey + 'y'] = mapKeyTo('redo'); keyHandlers[ctrlKey + "y"] = mapKeyTo("redo");
//keyHandlers[ctrlKey + 'z'] = mapKeyTo('undo'); //keyHandlers[ctrlKey + "z"] = mapKeyTo("undo");
keyHandlers[ctrlKey + 'shift-z'] = mapKeyTo('redo'); keyHandlers[ctrlKey + 'shift-z'] = mapKeyTo("redo");
keyHandlers['redo'] = mapKeyTo('redo'); keyHandlers["redo"] = mapKeyTo("redo");
//keyHandlers['undo'] = mapKeyTo('undo'); //keyHandlers["undo"] = mapKeyTo("undo");
class EditStack extends Array class EditStack extends Array
{ {
@ -2044,11 +2044,11 @@ class EditStack extends Array
} }
stateChanged(/*canUndo, canRedo*/) { stateChanged(/*canUndo, canRedo*/) {
this.squire.fireEvent('undoStateChange', { this.squire.fireEvent("undoStateChange", {
canUndo: this.index > 0, canUndo: this.index > 0,
canRedo: this.index + 1 < this.length canRedo: this.index + 1 < this.length
}); });
this.squire.fireEvent('input'); this.squire.fireEvent("input");
} }
docWasChanged() { docWasChanged() {
@ -2056,7 +2056,7 @@ class EditStack extends Array
this.inUndoState = false; this.inUndoState = false;
this.stateChanged(/*true, false*/); this.stateChanged(/*true, false*/);
} else } else
this.squire.fireEvent('input'); this.squire.fireEvent("input");
} }
/** /**
@ -2344,7 +2344,7 @@ class Squire
let handlers = this._events[type]; let handlers = this._events[type];
if (!handlers) { if (!handlers) {
handlers = this._events[type] = []; handlers = this._events[type] = [];
customEvents[type] || this._root.addEventListener(type, this, {capture:true,passive:'touchstart'===type}); customEvents[type] || this._root.addEventListener(type, this, {capture:true,passive:"touchstart"===type});
} }
handlers.push(fn); handlers.push(fn);
}); });
@ -2725,7 +2725,7 @@ class Squire
// After inserting the fragment, check whether the cursor is inside // After inserting the fragment, check whether the cursor is inside
// an <a> element and if so if there is an equivalent cursor // an <a> element and if so if there is an equivalent cursor
// position after the <a> element. If there is, move it there. // position after the <a> element. If there is, move it there.
moveRangeBoundaryOutOf(range, 'A', root); moveRangeBoundaryOutOf(range, "A", root);
this._ensureBottomLine(); this._ensureBottomLine();
@ -2778,7 +2778,7 @@ class Squire
} }
insertImage(src, attributes) { insertImage(src, attributes) {
const img = createElement('IMG', mergeObjects({ const img = createElement("IMG", mergeObjects({
src: src src: src
}, attributes, true)); }, attributes, true));
this.insertElement(img); this.insertElement(img);
@ -2787,7 +2787,7 @@ class Squire
insertPlainText(plainText, isPaste) { insertPlainText(plainText, isPaste) {
const range = this.getSelection(); const range = this.getSelection();
if (range.collapsed && getClosest(range.startContainer, this._root, 'PRE')) { if (range.collapsed && getClosest(range.startContainer, this._root, "PRE")) {
let node = range.startContainer; let node = range.startContainer;
let offset = range.startOffset; let offset = range.startOffset;
let text; let text;
@ -2969,8 +2969,8 @@ class Squire
range.commonAncestorContainer, range.commonAncestorContainer,
SHOW_ELEMENT_OR_TEXT, SHOW_ELEMENT_OR_TEXT,
node => (isTextNode(node) || node => (isTextNode(node) ||
node.nodeName === 'BR' || node.nodeName === "BR" ||
node.nodeName === 'IMG' node.nodeName === "IMG"
) && isNodeContainedInRange(range, node) ) && isNodeContainedInRange(range, node)
); );
@ -3078,7 +3078,7 @@ class Squire
// in a clone of the tag we're removing and we're done. // in a clone of the tag we're removing and we're done.
if (!isNodeContainedInRange(range, node)) { if (!isNodeContainedInRange(range, node)) {
// Ignore bookmarks and empty text nodes // Ignore bookmarks and empty text nodes
if (node.nodeName !== 'INPUT' && (!isText || node.data)) { if (node.nodeName !== "INPUT" && (!isText || node.data)) {
toWrap.push([exemplar, node]); toWrap.push([exemplar, node]);
} }
return; return;
@ -3139,12 +3139,12 @@ class Squire
} }
// --- // ---
bold() { this.toggleTag('B'); } bold() { this.toggleTag("B"); }
italic() { this.toggleTag('I'); } italic() { this.toggleTag("I"); }
underline() { this.toggleTag('U'); } underline() { this.toggleTag("U"); }
strikethrough() { this.toggleTag('S'); } strikethrough() { this.toggleTag("S"); }
subscript() { this.toggleTag('SUB', 'SUP'); } subscript() { this.toggleTag("SUB", "SUP"); }
superscript() { this.toggleTag('SUP', 'SUB'); } superscript() { this.toggleTag("SUP", "SUB"); }
makeLink(url, attributes) { makeLink(url, attributes) {
const range = this.getSelection(); const range = this.getSelection();
@ -3286,7 +3286,7 @@ class Squire
} }
block = getClosest(block, root, "LI") || block; block = getClosest(block, root, "LI") || block;
if (isEmptyBlock(block) && (parent = getClosest(block, root, 'UL,OL,BLOCKQUOTE'))) { if (isEmptyBlock(block) && (parent = getClosest(block, root, 'UL,OL,BLOCKQUOTE'))) {
return 'BLOCKQUOTE' === parent.nodeName return "BLOCKQUOTE" === parent.nodeName
// Break blockquote // Break blockquote
? self.modifyBlocks((/* frag */) => self.createDefaultBlock(createBookmarkNodes(self)), range) ? self.modifyBlocks((/* frag */) => self.createDefaultBlock(createBookmarkNodes(self)), range)
// Break list // Break list
@ -3395,7 +3395,7 @@ class Squire
do { do {
next = startLi === endLi ? null : startLi.nextSibling; next = startLi === endLi ? null : startLi.nextSibling;
newParent.append(startLi); newParent.append(startLi);
} while ((startLi = next)); } while (startLi = next);
next = newParent.nextSibling; next = newParent.nextSibling;
next && mergeContainers(next, root); next && mergeContainers(next, root);
@ -3429,7 +3429,7 @@ class Squire
list.nextSibling : list.nextSibling :
split(list, endLi.nextSibling, newParent, root); split(list, endLi.nextSibling, newParent, root);
if (newParent !== root && newParent.nodeName === 'LI') { if (newParent !== root && newParent.nodeName === "LI") {
newParent = newParent.parentNode; newParent = newParent.parentNode;
while (insertBefore) { while (insertBefore) {
next = insertBefore.nextSibling; next = insertBefore.nextSibling;
@ -3443,11 +3443,11 @@ class Squire
do { do {
next = startLi === endLi ? null : startLi.nextSibling; next = startLi === endLi ? null : startLi.nextSibling;
startLi.remove(); startLi.remove();
if (makeNotList && startLi.nodeName === 'LI') { if (makeNotList && startLi.nodeName === "LI") {
startLi = this.createDefaultBlock([empty(startLi)]); startLi = this.createDefaultBlock([empty(startLi)]);
} }
newParent.insertBefore(startLi, insertBefore); newParent.insertBefore(startLi, insertBefore);
} while ((startLi = next)); } while (startLi = next);
} }
list.firstChild || detach(list); list.firstChild || detach(list);
@ -3462,11 +3462,11 @@ class Squire
} }
makeUnorderedList() { makeUnorderedList() {
return this.modifyBlocks(frag => makeList(this, frag, 'UL')).focus(); return this.modifyBlocks(frag => makeList(this, frag, "UL")).focus();
} }
makeOrderedList() { makeOrderedList() {
return this.modifyBlocks(frag => makeList(this, frag, 'OL')).focus(); return this.modifyBlocks(frag => makeList(this, frag, "OL")).focus();
} }
removeList() { removeList() {
@ -3479,11 +3479,9 @@ class Squire
list.replaceWith(listFrag); list.replaceWith(listFrag);
}); });
frag.querySelectorAll('LI').forEach(item => { frag.querySelectorAll("LI").forEach(item => {
if (isBlock(item)) { if (isBlock(item)) {
item.replaceWith( item.replaceWith(this.createDefaultBlock([empty(item)]));
this.createDefaultBlock([empty(item)])
);
} else { } else {
fixContainer(item, root); fixContainer(item, root);
item.replaceWith(empty(item)); item.replaceWith(empty(item));
@ -3498,7 +3496,11 @@ class Squire
increaseQuoteLevel(range) { increaseQuoteLevel(range) {
return this.modifyBlocks( return this.modifyBlocks(
frag => createElement('BLOCKQUOTE', null, [frag]), frag => createElement(
"BLOCKQUOTE",
null,
[frag]
),
range range
).focus(); ).focus();
} }
@ -3506,9 +3508,11 @@ class Squire
decreaseQuoteLevel(range) { decreaseQuoteLevel(range) {
return this.modifyBlocks( return this.modifyBlocks(
frag => { frag => {
Array.prototype.filter.call(frag.querySelectorAll('blockquote'), el => Array.prototype.filter.call(frag.querySelectorAll("blockquote"), el =>
!getClosest(el.parentNode, frag, 'BLOCKQUOTE') !getClosest(el.parentNode, frag, "BLOCKQUOTE")
).forEach(el => el.replaceWith(empty(el))); ).forEach(el =>
el.replaceWith(empty(el))
);
return frag; return frag;
}, },
range range
@ -3526,59 +3530,60 @@ class Squire
let walker = getBlockWalker(frag, root); let walker = getBlockWalker(frag, root);
let node; let node;
// 1. Extract inline content; drop all blocks and contains. // 1. Extract inline content; drop all blocks and contains.
while ((node = walker.nextNode())) { while (node = walker.nextNode()) {
// 2. Replace <br> with \n in content // 2. Replace <br> with \n in content
node.querySelectorAll('BR').forEach(br => { node.querySelectorAll("BR").forEach(br => {
if (!isLineBreak(br, false)) { if (!isLineBreak(br, false)) {
detach(br); detach(br);
} else { } else {
br.replaceWith(doc.createTextNode('\n')); br.replaceWith(doc.createTextNode("\n"));
} }
}); });
// 3. Remove <code>; its format clashes with <pre> // 3. Remove <code>; its format clashes with <pre>
node.querySelectorAll('CODE').forEach(el => detach(el)); node.querySelectorAll("CODE").forEach(el => detach(el));
if (output.childNodes.length) { if (output.childNodes.length) {
output.append(doc.createTextNode('\n')); output.append(doc.createTextNode("\n"));
} }
output.append(empty(node)); output.append(empty(node));
} }
// 4. Replace nbsp with regular sp // 4. Replace nbsp with regular sp
walker = createTreeWalker(output, SHOW_TEXT); walker = createTreeWalker(output, SHOW_TEXT);
while ((node = walker.nextNode())) { while (node = walker.nextNode()) {
node.data = node.data.replace(NBSP, ' '); // nbsp -> sp node.data = node.data.replace(NBSP, ' '); // nbsp -> sp
} }
output.normalize(); output.normalize();
return fixCursor(createElement('PRE', return fixCursor(
null, [ createElement("PRE", null, [
output output
])); ])
);
}, range).focus(); }, range).focus();
} }
return this.changeFormat({ tag: 'CODE' }, null, range); return this.changeFormat({ tag: "CODE" }, null, range);
} }
removeCode() { removeCode() {
let range = this.getSelection(); let range = this.getSelection();
let ancestor = range.commonAncestorContainer; let ancestor = range.commonAncestorContainer;
let inPre = getClosest(ancestor, this._root, 'PRE'); let inPre = getClosest(ancestor, this._root, "PRE");
if (inPre) { if (inPre) {
return this.modifyBlocks(frag => { return this.modifyBlocks(frag => {
let root = this._root; let root = this._root;
let pres = frag.querySelectorAll('PRE'); let pres = frag.querySelectorAll("PRE");
let l = pres.length; let l = pres.length;
let pre, walker, node, value, contents, index; let pre, walker, node, value, contents, index;
while (l--) { while (l--) {
pre = pres[l]; pre = pres[l];
walker = createTreeWalker(pre, SHOW_TEXT); walker = createTreeWalker(pre, SHOW_TEXT);
while ((node = walker.nextNode())) { while (node = walker.nextNode()) {
value = node.data; value = node.data;
value = value.replace(/ (?=)/g, NBSP); // sp -> nbsp value = value.replace(/ (?=)/g, NBSP); // sp -> nbsp
contents = doc.createDocumentFragment(); contents = doc.createDocumentFragment();
while ((index = value.indexOf('\n')) > -1) { while ((index = value.indexOf("\n")) > -1) {
contents.append( contents.append(
doc.createTextNode(value.slice(0, index)) doc.createTextNode(value.slice(0, index))
); );
contents.append(createElement('BR')); contents.append(createElement("BR"));
value = value.slice(index + 1); value = value.slice(index + 1);
} }
node.before(contents); node.before(contents);
@ -3590,11 +3595,11 @@ class Squire
return frag; return frag;
}, range).focus(); }, range).focus();
} }
return this.changeFormat(null, { tag: 'CODE' }, range); return this.changeFormat(null, { tag: "CODE" }, range);
} }
toggleCode() { toggleCode() {
return (this.hasFormat('PRE') || this.hasFormat('CODE')) return (this.hasFormat("PRE") || this.hasFormat("CODE"))
? this.removeCode() ? this.removeCode()
: this.code(); : this.code();
} }
@ -3611,7 +3616,7 @@ class Squire
// --- Formatting --- // --- Formatting ---
setStyle(style) { setStyle(style) {
this.setAttribute('style', style); this.setAttribute("style", style);
} }
setAttribute(name, value) { setAttribute(name, value) {
@ -3619,7 +3624,7 @@ class Squire
let start = range?.startContainer || {}; let start = range?.startContainer || {};
let end = range ? range.endContainer : 0; let end = range ? range.endContainer : 0;
// When the selection is all the text inside an element, set style on the element itself // When the selection is all the text inside an element, set style on the element itself
if ('dir' == name || (isTextNode(start) && 0 === range.startOffset && start === end && end.length === range.endOffset)) { if ("dir" == name || (isTextNode(start) && 0 === range.startOffset && start === end && end.length === range.endOffset)) {
this.saveUndoState(range); this.saveUndoState(range);
setAttributes(start.parentNode, {[name]: value}); setAttributes(start.parentNode, {[name]: value});
this.setRange(range); this.setRange(range);
@ -3627,7 +3632,7 @@ class Squire
// Else create a span element // Else create a span element
else { else {
this.changeFormat({ this.changeFormat({
tag: 'SPAN', tag: "SPAN",
attributes: {[name]: value} attributes: {[name]: value}
}, null, range); }, null, range);
} }
@ -3638,9 +3643,9 @@ class Squire
changeIndentationLevel(direction) { changeIndentationLevel(direction) {
let parent = this.getSelectionClosest('UL,OL,BLOCKQUOTE'); let parent = this.getSelectionClosest('UL,OL,BLOCKQUOTE');
if (parent || 'increase' === direction) { if (parent || "increase" === direction) {
let method = (!parent || 'BLOCKQUOTE' === parent.nodeName) ? 'Quote' : 'List'; let method = (!parent || "BLOCKQUOTE" === parent.nodeName) ? "Quote" : "List";
this[direction + method + 'Level'](); this[direction + method + "Level"]();
} }
} }