Cleanup unused Squire code

This commit is contained in:
the-djmaze 2023-02-20 13:12:09 +01:00
parent e56d1cf5c6
commit 8a63ae05ef
2 changed files with 1257 additions and 1370 deletions

View file

@ -27,11 +27,6 @@ const
SquireDefaultConfig = { SquireDefaultConfig = {
/* /*
blockTag: 'P',
undo: {
documentSizeThreshold: -1, // -1 means no threshold
undoLimit: -1 // -1 means no limit
},
addLinks: true // allow_smart_html_links addLinks: true // allow_smart_html_links
*/ */
sanitizeToDOMFragment: (html, isPaste/*, squire*/) => { sanitizeToDOMFragment: (html, isPaste/*, squire*/) => {
@ -379,7 +374,6 @@ class SquireUI
squire.addEventListener('cursor', ); squire.addEventListener('cursor', );
squire.addEventListener('select', ); squire.addEventListener('select', );
squire.addEventListener('input', ); squire.addEventListener('input', );
squire.addEventListener('willPaste', );
squire.addEventListener( 'keydown keyup', monitorShiftKey ) squire.addEventListener( 'keydown keyup', monitorShiftKey )
squire.addEventListener( 'keydown', onKey ) squire.addEventListener( 'keydown', onKey )
*/ */

View file

@ -11,6 +11,7 @@
(doc => { (doc => {
const const
blockTag = 'DIV',
DOCUMENT_POSITION_PRECEDING = 2, // Node.DOCUMENT_POSITION_PRECEDING DOCUMENT_POSITION_PRECEDING = 2, // Node.DOCUMENT_POSITION_PRECEDING
ELEMENT_NODE = 1, // Node.ELEMENT_NODE, ELEMENT_NODE = 1, // Node.ELEMENT_NODE,
TEXT_NODE = 3, // Node.TEXT_NODE, TEXT_NODE = 3, // Node.TEXT_NODE,
@ -152,10 +153,10 @@ const
return null; return null;
}, },
getPath = ( node, root, config ) => { getPath = (node, root) => {
let path = '', style; let path = '', style;
if (node && node !== root) { if (node && node !== root) {
path = getPath( node.parentNode, root, config ); path = getPath(node.parentNode, root);
if (node.nodeType === ELEMENT_NODE) { if (node.nodeType === ELEMENT_NODE) {
path += (path ? '>' : '') + node.nodeName; path += (path ? '>' : '') + node.nodeName;
if (node.id) { if (node.id) {
@ -284,7 +285,7 @@ const
[...container.children].forEach(child => { [...container.children].forEach(child => {
isBR = child.nodeName === 'BR'; isBR = child.nodeName === 'BR';
if (!isBR && isInline(child) if (!isBR && isInline(child)
// && (root.__squire__._config.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);
@ -1070,8 +1071,7 @@ const
node = node.parentNode; node = node.parentNode;
} }
parent = node; parent = node;
while ( isInline( parent ) && while (isInline(parent) && (!parent.textContent || parent.textContent === ZWS)) {
( !parent.textContent || parent.textContent === ZWS ) ) {
node = parent; node = parent;
parent = node.parentNode; parent = node.parentNode;
} }
@ -1095,8 +1095,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 && if (node === self._root && (node = node.firstChild) && node.nodeName === 'BR') {
( node = node.firstChild ) && node.nodeName === 'BR' ) {
detach(node); detach(node);
} }
self._ensureBottomLine(); self._ensureBottomLine();
@ -1126,9 +1125,7 @@ const
// Remove any zws so we don't think there's content in an empty // Remove any zws so we don't think there's content in an empty
// block. // block.
self._recordUndoState(range, false); self._recordUndoState(range, false);
if ( self._config.addLinks ) { self._config.addLinks && addLinks(range.startContainer, root, self);
addLinks( range.startContainer, root, self );
}
self._removeZWS(); self._removeZWS();
self._getRangeAndRemoveBookmark(range); self._getRangeAndRemoveBookmark(range);
@ -1229,9 +1226,7 @@ const
// Don't continue links over a block break; unlikely to be the // Don't continue links over a block break; unlikely to be the
// desired outcome. // desired outcome.
if ( nodeAfterSplit.nodeName === 'A' && if (nodeAfterSplit.nodeName === 'A' && (!nodeAfterSplit.textContent || nodeAfterSplit.textContent === ZWS)) {
( !nodeAfterSplit.textContent ||
nodeAfterSplit.textContent === ZWS ) ) {
child = doc.createTextNode(''); child = doc.createTextNode('');
nodeAfterSplit.replaceWith(child); nodeAfterSplit.replaceWith(child);
nodeAfterSplit = child; nodeAfterSplit = child;
@ -1436,7 +1431,7 @@ const
and whitespace nodes. and whitespace nodes.
2. Convert inline tags into our preferred format. 2. Convert inline tags into our preferred format.
*/ */
cleanTree = ( node, config, preserveWS ) => { cleanTree = (node, preserveWS) => {
let children = node.childNodes, let children = node.childNodes,
nonInlineParent, i, l, child, nodeName, nodeType, childLength; nonInlineParent, i, l, child, nodeName, nodeType, childLength;
// startsWithWS, endsWithWS, data, sibling; // startsWithWS, endsWithWS, data, sibling;
@ -1467,8 +1462,7 @@ const
continue; continue;
} }
if (childLength) { if (childLength) {
cleanTree( child, config, cleanTree(child, preserveWS || (nodeName === 'PRE'));
preserveWS || ( nodeName === 'PRE' ) );
} }
/* /*
} else { } else {
@ -1485,9 +1479,7 @@ const
walker.currentNode = child; walker.currentNode = child;
while (sibling = previousPONode(walker)) { while (sibling = previousPONode(walker)) {
nodeName = sibling.nodeName; nodeName = sibling.nodeName;
if ( nodeName === 'IMG' || if (nodeName === 'IMG' || (nodeName === '#text' && notWS.test(sibling.data))) {
( nodeName === '#text' &&
notWS.test( sibling.data ) ) ) {
break; break;
} }
if (!isInline(sibling)) { if (!isInline(sibling)) {
@ -1500,9 +1492,7 @@ const
if (endsWithWS) { if (endsWithWS) {
walker.currentNode = child; walker.currentNode = child;
while (sibling = walker.nextNode()) { while (sibling = walker.nextNode()) {
if ( nodeName === 'IMG' || if (nodeName === 'IMG' || (nodeName === '#text' && notWS.test(sibling.data))) {
( nodeName === '#text' &&
notWS.test( sibling.data ) ) ) {
break; break;
} }
if (!isInline(sibling)) { if (!isInline(sibling)) {
@ -1555,8 +1545,7 @@ const
} }
walker = createTreeWalker(block, SHOW_ELEMENT_OR_TEXT, notWSTextNode); walker = createTreeWalker(block, SHOW_ELEMENT_OR_TEXT, notWSTextNode);
walker.currentNode = br; walker.currentNode = br;
return !!walker.nextNode() || return !!walker.nextNode() || (isLBIfEmptyBlock && !walker.previousNode());
( isLBIfEmptyBlock && !walker.previousNode() );
}, },
// <br> elements are treated specially, and differently depending on the // <br> elements are treated specially, and differently depending on the
@ -1589,8 +1578,7 @@ const
// The (non-standard but supported enough) innerText property is based on the // The (non-standard but supported enough) innerText property is based on the
// render tree in Firefox and possibly other browsers, so we must insert the // render tree in Firefox and possibly other browsers, so we must insert the
// DOM node into the document to ensure the text part is correct. // DOM node into the document to ensure the text part is correct.
setClipboardData = setClipboardData = (event, contents, root) => {
( 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');
@ -1650,8 +1638,7 @@ const
let walker = createTreeWalker(root, SHOW_TEXT); let walker = createTreeWalker(root, SHOW_TEXT);
let parent, node, index; let parent, node, index;
while (node = walker.nextNode()) { while (node = walker.nextNode()) {
while ( ( index = node.data.indexOf( ZWS ) ) > -1 && while ((index = node.data.indexOf(ZWS)) > -1 && (!keepNode || node.parentNode !== keepNode)) {
( !keepNode || node.parentNode !== keepNode ) ) {
if (node.length === 1) { if (node.length === 1) {
do { do {
parent = node.parentNode; parent = node.parentNode;
@ -1693,7 +1680,7 @@ const
}, },
splitBlock = (self, block, node, offset) => { splitBlock = (self, block, node, offset) => {
let splitTag = tagAfterSplit[ block.nodeName ] || self._config.blockTag, let splitTag = tagAfterSplit[ block.nodeName ] || blockTag,
nodeAfterSplit = split(node, offset, block.parentNode, self._root); nodeAfterSplit = split(node, offset, block.parentNode, self._root);
// Make sure the new node is the correct type. // Make sure the new node is the correct type.
@ -1838,9 +1825,7 @@ function onKey ( event ) {
if (this._keyHandlers[ key ]) { if (this._keyHandlers[ key ]) {
this._keyHandlers[ key ](this, event, range); this._keyHandlers[ key ](this, event, range);
// !event.isComposing stops us from blatting Kana-Kanji conversion in Safari // !event.isComposing stops us from blatting Kana-Kanji conversion in Safari
} else if ( !range.collapsed && !event.isComposing && } else if (!range.collapsed && !event.isComposing && !event[osKey] && key.length === 1) {
!event[osKey] &&
key.length === 1 ) {
// Record undo checkpoint. // Record undo checkpoint.
this.saveUndoState(range); this.saveUndoState(range);
// Delete the selection // Delete the selection
@ -2207,9 +2192,7 @@ let keyHandlers = {
space: (self, _, range) => { space: (self, _, range) => {
let root = self._root; let root = self._root;
self._recordUndoState(range, false); self._recordUndoState(range, false);
if ( self._config.addLinks ) { self._config.addLinks && addLinks(range.startContainer, root, self);
addLinks( range.startContainer, root, self );
}
self._getRangeAndRemoveBookmark(range); self._getRangeAndRemoveBookmark(range);
/* /*
// If the cursor is at the end of a link (<a>foo|</a>) then move it // If the cursor is at the end of a link (<a>foo|</a>) then move it
@ -2223,8 +2206,7 @@ let keyHandlers = {
range.setStartAfter(node); range.setStartAfter(node);
break; break;
} }
} while ( !node.nextSibling && } while (!node.nextSibling && (node = node.parentNode) && node !== root);
( node = node.parentNode ) && node !== root );
} }
*/ */
// Delete the selection if not collapsed // Delete the selection if not collapsed
@ -2474,27 +2456,15 @@ class Squire
} }
setConfig(config) { setConfig(config) {
config = mergeObjects({ this._config = mergeObjects({
blockTag: 'DIV',
addLinks: true addLinks: true
}, config, true); }, config, true);
if ( typeof config.sanitizeToDOMFragment !== 'function' ) {
config.sanitizeToDOMFragment = null;
}
// Users may specify block tag in lower case
config.blockTag = config.blockTag.toUpperCase();
this._config = config;
return this; return this;
} }
createDefaultBlock(children) { createDefaultBlock(children) {
let config = this._config;
return fixCursor( return fixCursor(
createElement( config.blockTag, null, children ), createElement(blockTag, null, children),
this._root this._root
); );
} }
@ -2527,9 +2497,7 @@ class Squire
} }
} }
if (handlers) { if (handlers) {
if ( !event ) { event = event || {};
event = {};
}
if (event.type !== type) { if (event.type !== type) {
event.type = type; event.type = type;
} }
@ -2539,11 +2507,7 @@ class Squire
while (l--) { while (l--) {
obj = handlers[l]; obj = handlers[l];
try { try {
if ( obj.handleEvent ) { obj.handleEvent ? obj.handleEvent(event) : obj.call(this, event);
obj.handleEvent( event );
} else {
obj.call( this, event );
}
} catch (error) { } catch (error) {
error.details = 'Squire: fireEvent error. Event type: ' + type; error.details = 'Squire: fireEvent error. Event type: ' + type;
didError(error); didError(error);
@ -2699,12 +2663,11 @@ class Squire
let anchor = range.startContainer, let anchor = range.startContainer,
focus = range.endContainer, focus = range.endContainer,
newPath; newPath;
if ( force || anchor !== this._lastAnchorNode || if (force || anchor !== this._lastAnchorNode || focus !== this._lastFocusNode) {
focus !== this._lastFocusNode ) {
this._lastAnchorNode = anchor; this._lastAnchorNode = anchor;
this._lastFocusNode = focus; this._lastFocusNode = focus;
newPath = (anchor && focus) ? (anchor === focus ? newPath = (anchor && focus) ? (anchor === focus ?
getPath( focus, this._root, this._config ) : '(selection)' ) : ''; getPath(focus, this._root) : '(selection)') : '';
if (this._path !== newPath) { if (this._path !== newPath) {
this._path = newPath; this._path = newPath;
this.fireEvent('pathChange', { path: newPath }); this.fireEvent('pathChange', { path: newPath });
@ -2739,8 +2702,7 @@ class Squire
insertNodeInRange(range, endNode); insertNodeInRange(range, endNode);
// In a collapsed range, the start is sometimes inserted after the end! // In a collapsed range, the start is sometimes inserted after the end!
if ( startNode.compareDocumentPosition( endNode ) & if (startNode.compareDocumentPosition(endNode) & DOCUMENT_POSITION_PRECEDING) {
DOCUMENT_POSITION_PRECEDING ) {
startNode.id = endSelectionId; startNode.id = endSelectionId;
endNode.id = startSelectionId; endNode.id = startSelectionId;
temp = startNode; temp = startNode;
@ -2913,8 +2875,7 @@ class Squire
fontInfo.color = attr; fontInfo.color = attr;
++seenAttributes; ++seenAttributes;
} }
if ( !fontInfo.backgroundColor && if (!fontInfo.backgroundColor && (attr = style.backgroundColor)) {
( attr = style.backgroundColor ) ) {
fontInfo.backgroundColor = attr; fontInfo.backgroundColor = attr;
++seenAttributes; ++seenAttributes;
} }
@ -3077,8 +3038,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' && if (node.nodeName !== 'INPUT' && (!isText || node.data)) {
( !isText || node.data ) ) {
toWrap.push([ exemplar, node ]); toWrap.push([ exemplar, node ]);
} }
return; return;
@ -3311,8 +3271,7 @@ class Squire
_ensureBottomLine () { _ensureBottomLine () {
let root = this._root; let root = this._root;
let last = root.lastElementChild; let last = root.lastElementChild;
if ( !last || if (!last || last.nodeName !== blockTag || !isBlock(last)) {
last.nodeName !== this._config.blockTag || !isBlock( last ) ) {
root.append(this.createDefaultBlock()); root.append(this.createDefaultBlock());
} }
} }
@ -3326,13 +3285,8 @@ class Squire
_setHTML (html) { _setHTML (html) {
let root = this._root; let root = this._root;
let node = root; let node = root;
let sanitizeToDOMFragment = this._config.sanitizeToDOMFragment; empty(root);
if ( sanitizeToDOMFragment ) { root.appendChild(this._config.sanitizeToDOMFragment(html, false));
empty( node );
node.appendChild( sanitizeToDOMFragment( html, false, this ) );
} else {
node.innerHTML = html;
}
do { do {
fixCursor(node, root); fixCursor(node, root);
} while (node = getNextBlock(node, root)); } while (node = getNextBlock(node, root));
@ -3350,22 +3304,12 @@ class Squire
} }
setHTML(html) { setHTML(html) {
let config = this._config; let root = this._root,
let sanitizeToDOMFragment = config.sanitizeToDOMFragment;
let root = this._root;
let div, frag, child;
// Parse HTML into DOM tree // Parse HTML into DOM tree
if ( sanitizeToDOMFragment ) { frag = this._config.sanitizeToDOMFragment(html, false),
frag = sanitizeToDOMFragment( html, false, this ); child;
} else {
div = createElement( 'DIV' );
div.innerHTML = html;
frag = doc.createDocumentFragment();
frag.append( empty( div ) );
}
cleanTree( frag, config ); cleanTree(frag);
cleanupBRs(frag, root, false); cleanupBRs(frag, root, false);
fixContainer(frag, root); fixContainer(frag, root);
@ -3459,55 +3403,28 @@ class Squire
// insertTreeFragmentIntoRange will delete the selection so that it is replaced // insertTreeFragmentIntoRange will delete the selection so that it is replaced
// by the html being inserted. // by the html being inserted.
insertHTML(html, isPaste) { insertHTML(html, isPaste) {
let config = this._config;
let sanitizeToDOMFragment = config.sanitizeToDOMFragment;
let range = this.getSelection(); let range = this.getSelection();
let startFragmentIndex, endFragmentIndex;
let div, frag, root, node, event;
// Edge doesn't just copy the fragment, but includes the surrounding guff // Edge doesn't just copy the fragment, but includes the surrounding guff
// including the full <head> of the page. Need to strip this out. // including the full <head> of the page. Need to strip this out.
if (isPaste) { if (isPaste) {
startFragmentIndex = html.indexOf( '<!--StartFragment-->' ); let startFragmentIndex = html.indexOf('<!--StartFragment-->'),
endFragmentIndex = html.lastIndexOf('<!--EndFragment-->'); endFragmentIndex = html.lastIndexOf('<!--EndFragment-->');
if (startFragmentIndex > -1 && endFragmentIndex > -1) { if (startFragmentIndex > -1 && endFragmentIndex > -1) {
html = html.slice(startFragmentIndex + 20, endFragmentIndex); html = html.slice(startFragmentIndex + 20, endFragmentIndex);
} }
} }
if ( sanitizeToDOMFragment ) {
frag = sanitizeToDOMFragment( html, isPaste, this ); let frag = this._config.sanitizeToDOMFragment(html, isPaste);
} else {
// Wrap with <tr> if html contains dangling <td> tags
if ( /<\/td>((?!<\/tr>)[\s\S])*$/i.test( html ) ) {
html = '<TR>' + html + '</TR>';
}
// Wrap with <table> if html contains dangling <tr> tags
if ( /<\/tr>((?!<\/table>)[\s\S])*$/i.test( html ) ) {
html = '<TABLE>' + html + '</TABLE>';
}
// Parse HTML into DOM tree
div = createElement( 'DIV' );
div.innerHTML = html;
frag = doc.createDocumentFragment();
frag.append( empty( div ) );
}
// Record undo checkpoint // Record undo checkpoint
this.saveUndoState(range); this.saveUndoState(range);
try { try {
root = this._root; let root = this._root, node = frag;
node = frag;
event = {
fragment: frag,
preventDefault: function () {
this.defaultPrevented = true;
},
defaultPrevented: false
};
addLinks(frag, frag, this); addLinks(frag, frag, this);
cleanTree( frag, config ); cleanTree(frag);
cleanupBRs(frag, root, false); cleanupBRs(frag, root, false);
removeEmptyInlines(frag); removeEmptyInlines(frag);
frag.normalize(); frag.normalize();
@ -3516,10 +3433,7 @@ class Squire
fixCursor(node, root); fixCursor(node, root);
} }
isPaste && this.fireEvent( 'willPaste', event ); insertTreeFragmentIntoRange(range, frag, root);
if ( !event.defaultPrevented ) {
insertTreeFragmentIntoRange( range, event.fragment, root );
range.collapse(false); range.collapse(false);
// After inserting the fragment, check whether the cursor is inside // After inserting the fragment, check whether the cursor is inside
@ -3528,7 +3442,6 @@ class Squire
moveRangeBoundaryOutOf(range, 'A', root); moveRangeBoundaryOutOf(range, 'A', root);
this._ensureBottomLine(); this._ensureBottomLine();
}
this.setSelection(range); this.setSelection(range);
this._updatePath(range, true); this._updatePath(range, true);
@ -3542,41 +3455,28 @@ class Squire
insertPlainText(plainText, isPaste) { insertPlainText(plainText, isPaste) {
let range = this.getSelection(); let range = this.getSelection();
if ( range.collapsed && if (range.collapsed && getClosest(range.startContainer, this._root, 'PRE')) {
getClosest( range.startContainer, this._root, 'PRE' ) ) {
let node = range.startContainer; let node = range.startContainer;
let offset = range.startOffset; let offset = range.startOffset;
let text, event; let text;
if (node?.nodeType !== TEXT_NODE) { if (node?.nodeType !== TEXT_NODE) {
text = doc.createTextNode(''); text = doc.createTextNode('');
node?.childNodes[ offset ].before(text); node?.childNodes[ offset ].before(text);
node = text; node = text;
offset = 0; offset = 0;
} }
event = {
text: plainText,
preventDefault: function () {
this.defaultPrevented = true;
},
defaultPrevented: false
};
isPaste && this.fireEvent( 'willPaste', event );
if ( !event.defaultPrevented ) {
plainText = event.text;
node.insertData(offset, plainText); node.insertData(offset, plainText);
range.setStart(node, offset + plainText.length); range.setStart(node, offset + plainText.length);
range.collapse(true); range.collapse(true);
}
this.setSelection(range); this.setSelection(range);
return this; return this;
} }
let lines = plainText.split( '\n' ); let lines = plainText.split(/\r?\n/),
let config = this._config; closeBlock = '</' + blockTag + '>',
let tag = config.blockTag; openBlock = '<' + blockTag + '>',
let closeBlock = '</' + tag + '>'; i = lines.length,
let openBlock = '<' + tag + '>'; line;
let i = lines.length, line;
while (i--) { while (i--) {
line = escapeHTML(lines[i]).replace(/ (?=)/g, '&nbsp;'); line = escapeHTML(lines[i]).replace(/ (?=)/g, '&nbsp;');
@ -3596,13 +3496,9 @@ class Squire
makeLink(url, attributes) { makeLink(url, attributes) {
let range = this.getSelection(); let range = this.getSelection();
if (range.collapsed) { if (range.collapsed) {
let protocolEnd = url.indexOf( ':' ) + 1;
if ( protocolEnd ) {
while ( url[ protocolEnd ] === '/' ) { ++protocolEnd; }
}
insertNodeInRange( insertNodeInRange(
range, range,
doc.createTextNode( url.slice( protocolEnd ) ) doc.createTextNode(url.replace(/^[^:]*:\/*/, ''))
); );
} }
attributes = mergeObjects( attributes = mergeObjects(
@ -3737,12 +3633,9 @@ class Squire
} }
toggleCode() { toggleCode() {
if ( this.hasFormat( 'PRE' ) || this.hasFormat( 'CODE' ) ) { return (this.hasFormat('PRE') || this.hasFormat('CODE'))
this.removeCode(); ? this.removeCode()
} else { : this.code();
this.code();
}
return this;
} }
// --- // ---