aboutsummaryrefslogtreecommitdiffstats
path: root/src/rich-text.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/rich-text.js')
-rw-r--r--src/rich-text.js38
1 files changed, 26 insertions, 12 deletions
diff --git a/src/rich-text.js b/src/rich-text.js
index 7003f5c..b766767 100644
--- a/src/rich-text.js
+++ b/src/rich-text.js
@@ -46,20 +46,34 @@ function offsetOf(root, node, offset) {
return t.length;
}
-// (node, offset) for a text position; the field is normalised to text nodes
-// and <br>s, so that is all this walks.
+// (node, offset) for a text position, counted the way textOf counts: text,
+// <br>s, and the line break a new block (<div>, <p>) stands for — Chrome
+// wraps lines in <div>s once Enter has been pressed, and a walk of the top
+// level alone put every later position at the end of the field.
function pointAt(root, pos) {
- let left = pos;
- for (const c of root.childNodes) {
- if (c.nodeType === 3) {
- if (left <= c.data.length) return [c, left];
- left -= c.data.length;
- } else if (c.nodeName === 'BR') {
- if (left === 0) return [root, [...root.childNodes].indexOf(c)];
- left -= 1;
+ let left = pos, out = '';
+ const walk = (n) => {
+ const kids = [...n.childNodes];
+ for (let i = 0; i < kids.length; i++) {
+ const c = kids[i];
+ if (c.nodeType === 3) {
+ if (left <= c.data.length) return [c, left];
+ left -= c.data.length; out += c.data;
+ } else if (c.nodeName === 'BR') {
+ if (left === 0) return [n, i];
+ left -= 1; out += '\n';
+ } else if (c.nodeType === 1) {
+ if (BLOCK.has(c.nodeName) && out && !out.endsWith('\n')) {
+ if (left === 0) return [n, i];
+ left -= 1; out += '\n';
+ }
+ const got = walk(c);
+ if (got) return got;
+ }
}
- }
- return [root, root.childNodes.length];
+ return null;
+ };
+ return walk(root) || [root, root.childNodes.length];
}
const mediaOf = (dt) => [...((dt && dt.files) || [])].filter((f) => /^(image|video)\//.test(f.type));