node-quill-converter
Version:
Convert HTML to Delta or Delta to HTML
221 lines (155 loc) • 6.17 kB
Markdown
## OBJECTIVE
Recreate memory issue
refactor: prevent hanging execution
Instantiating a Quill instance at the module scope caused execution
of the module to hang indefinitely. Prior to Quill v1 there was a
destroy method to clean up instances however it was removed.
Removing the node in JSDOM did not resolve the issue.
# OBJECTIVE
Option to import katex.
# OBJECTIVE
Does including KaTeX enable formulas to go
from Delta to HTML?
TASKS
* Hard code KaTeX same as quill
* Set up test
https://github.com/KillerCodeMonkey/ngx-quill/issues/80
# OBJECTIVE
How do we optionally include formula library? Currently JSDOM_TEMPLATE is formed on require, not inside a function call. This is to avoid creating a JSDOM instance on each function call.
TODO: cache DOM
TODO: cache quill editor
TODO: Use local versioned scripts rather than CDN
Problem 1: We don't know why Quill isn't available after instantiation
Possible: External scripts
Any way to get ride of setTimeout/polling?
localScripts?
# FLOX
## OBJECTIVE
Inline of quill doesn't work in package
require('').resolve etc etc
---
# FLOX
setText set's text not HTML
https://quilljs.com/docs/modules/clipboard/#dangerouslypastehtml
ALTERNATIVE
All we are trying to do is create a delta from HTML.
Quill has implented this is their dangerouslyPasteHtml method.
We can't use JSDOM because they don't support getRange APIs.
Therefore we may be able to tease out their algorithm for converting HTML to a delta
and drop all the selection crap.
This is good because we could then potentially drop JSDOM all together.
https://github.com/quilljs/quill/blob/f83ae57131e3c3abd3913acf57c045bdb3340bfa/modules/clipboard.js#L75
# Flox
## OBJECTIVE
Passing non string to function throws error
Error: [Parchment] Unable to create html blot
at new e (about:blank:7:3081)
at Object.r [as create] (about:blank:7:1278)
convertHtmlToDelta(html, [options]
# FLOX
## OBJECTIVE
What the hell is this s { ops: []}
# FLOX
## OBJECTIVE
Tests for convertHtmlToDelta
# FLOX
## OBJECTIVE
Tests for convertDeltaToHtml
# FLOX
## OBJECTIVE
Comment by document.getSelected, document.execCommand are needed
# FLOX
## OBJECTIVE
Spread the word!
// https://github.com/zenoamaro/react-quill/blob/af34e52d8574737bb768e1517dc4610bee65db33/src/mixin.js#L68
// TODO: Add quill-delta
// TODO: Import Delta import Delta from 'quill-delta'
// TODO: Convert this.prepareMatching to pure function
// TODO: DOM tree library -> Cheerio?
function convert(html) {
// IF STRING convert to DOM tree
if (typeof html === 'string') {
this.container.innerHTML = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
return this.convert();
}
// IF CODE BLOCK, create simple delta and return
const formats = this.quill.getFormat(this.quill.selection.savedRange.index);
if (formats[CodeBlock.blotName]) {
const text = this.container.innerText;
this.container.innerHTML = '';
return new Delta().insert(text, { [CodeBlock.blotName]: formats[CodeBlock.blotName] });
}
// Parse DOM tree (this.container is your DOM tree) and create delta
let [elementMatchers, textMatchers] = this.prepareMatching();
let delta = traverse(this.container, elementMatchers, textMatchers);
// Remove trailing newline from delta
if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));
}
// Empty Quill container which was used as temp holding for DOM tree
this.container.innerHTML = '';
return delta;
}
// TODO: Understand this.prepareMatching
// What is this.matchers
// What is Node.TEXT_NODE
// What is this.container.querySelectorAll
function prepareMatching() {
let elementMatchers = [], textMatchers = [];
this.matchers.forEach((pair) => {
let [selector, matcher] = pair;
switch (selector) {
case Node.TEXT_NODE:
textMatchers.push(matcher);
break;
case Node.ELEMENT_NODE:
elementMatchers.push(matcher);
break;
default:
[].forEach.call(this.container.querySelectorAll(selector), (node) => {
// TODO use weakmap
node[DOM_KEY] = node[DOM_KEY] || [];
node[DOM_KEY].push(matcher);
});
break;
}
});
return [elementMatchers, textMatchers];
}
// THESE TWO ARE pure functions should be good to co as long as we get args right
// https://github.com/quilljs/quill/blob/f83ae57131e3c3abd3913acf57c045bdb3340bfa/modules/clipboard.js#L176
function deltaEndsWith(delta, text) {
let endText = "";
for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {
let op = delta.ops[i];
if (typeof op.insert !== 'string') break;
endText = op.insert + endText;
}
return endText.slice(-1*text.length) === text;
}
// https://github.com/quilljs/quill/blob/f83ae57131e3c3abd3913acf57c045bdb3340bfa/modules/clipboard.js#L192
function traverse(node, elementMatchers, textMatchers) { // Post-order
if (node.nodeType === node.TEXT_NODE) {
return textMatchers.reduce(function(delta, matcher) {
return matcher(node, delta);
}, new Delta());
} else if (node.nodeType === node.ELEMENT_NODE) {
return [].reduce.call(node.childNodes || [], (delta, childNode) => {
let childrenDelta = traverse(childNode, elementMatchers, textMatchers);
if (childNode.nodeType === node.ELEMENT_NODE) {
childrenDelta = elementMatchers.reduce(function(childrenDelta, matcher) {
return matcher(childNode, childrenDelta);
}, childrenDelta);
childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {
return matcher(childNode, childrenDelta);
}, childrenDelta);
}
return delta.concat(childrenDelta);
}, new Delta());
} else {
return new Delta();
}
}
I put together a node package to convert html/plain text to and from a Quill delta.
See [node-quill-converter](https://www.npmjs.com/package/node-quill-converter).
The package uses JSDOM similar to a previous comments example however it uses JSDOMs latest API