Handle invisible nodes in block-extending
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Sun, 10 Jul 2011 12:08:36 -0600
changeset 384 544ee95a17ff
parent 383 09930358b11f
child 385 2eccea436eb6
Handle invisible nodes in block-extending

This improves results not only for the comment-related tests I added
(which are corner cases), but also for "<p>foo<br>{}</p><p>extra</p>".
Previously it would do nothing, now it wraps the first <p>. Granted,
that's also a corner case, since such selections are pathological, but
I've pretty much finished speccing the non-corner-case stuff, so . . .
editcommands.html
implementation.js
source.html
tests.js
--- a/editcommands.html	Sun Jul 10 11:39:52 2011 -0600
+++ b/editcommands.html	Sun Jul 10 12:08:36 2011 -0600
@@ -3272,24 +3272,27 @@
     of <var title="">start node</var> and then set <var title="">start node</var> to its
     <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
 
-    <li>Otherwise, if <var title="">start offset</var> is <var title="">start node</var>'s
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> and <var title="">start node</var>'s last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is an
-    <a href=#inline-node>inline node</a> that's not a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, subtract one from <var title="">start
-    offset</var>.
+    <li>Otherwise, if <var title="">start node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
+    <var title="">start offset</var> minus one is <a href=#invisible>invisible</a>, subtract one
+    from <var title="">start offset</var>.
+
+    <li>Otherwise, if <var title="">start node</var> has no <a href=#visible>visible</a>
+    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> greater than or equal to <var title="">start
+    offset</var> and <var title="">start node</var>'s last <a href=#visible>visible</a>
+    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is an <a href=#inline-node>inline node</a> that's not a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, subtract one
+    from <var title="">start offset</var>.
     <!-- So if you have a collapsed selection at the end of a block, for
     instance, it will extend backwards into a block. -->
 
-    <li>Otherwise, if <var title="">start node</var> has a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
-    <var title="">start offset</var>, and that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> and its <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> are
-    both <a href=#inline-node title="inline node">inline nodes</a> and the
-    <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> isn't a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, subtract one from <var title="">start
-    offset</var>.
+    <li>Otherwise, if <var title="">start node</var> has a <a href=#visible>visible</a>
+    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> greater than or equal to <var title="">start offset</var>,
+    and the first such <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is an <a href=#inline-node>inline node</a>, and <var title="">start
+    node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">start offset</var> minus one is
+    an <a href=#inline-node>inline node</a> other than a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, subtract one from
+    <var title="">start offset</var>.
     <!-- IE also includes <br> (at least for the purposes of the indent
     command), but this is unlikely to match user expectations. -->
 
-    <p class=XXX>This is wrong in the presence of comments or display: none or
-    probably other things.  Do we care?
-
     <li>Otherwise, break from this loop.
   </ol>
 
@@ -3305,15 +3308,19 @@
     <var title="">end offset</var> to one plus the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">end node</var> and
     then set <var title="">end node</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
 
-    <li>Otherwise, if <var title="">end offset</var> is 0 and <var title="">end node</var>'s
-    first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is an <a href=#inline-node>inline node</a> that's not a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, add one
-    to <var title="">end offset</var>.
-
-    <li>Otherwise, if <var title="">end node</var> has a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
-    <var title="">end offset</var>, and that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> and its <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> are
-    both <a href=#inline-node title="inline node">inline nodes</a>, and the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of
-    <var title="">end node</var> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">end offset</var> isn't a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>,
-    add one to <var title="">end offset</var>.
+    <li>Otherwise, if <var title="">end node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">end
+    offset</var> is <a href=#invisible>invisible</a>, add one to <var title="">end offset</var>.
+
+    <li>Otherwise, if <var title="">end node</var> has no <a href=#visible>visible</a>
+    <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> less than <var title="">end offset</var> and <var title="">end
+    node</var>'s first <a href=#visible>visible</a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is an <a href=#inline-node>inline
+    node</a> that's not a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, add one to <var title="">end offset</var>.
+
+    <li>Otherwise, if <var title="">end node</var> has a <a href=#visible>visible</a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>
+    with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> less than <var title="">end offset</var>, and the last such <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>
+    is an <a href=#inline-node>inline node</a>, and <var title="">end node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with
+    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> <var title="">end offset</var> is an <a href=#inline-node>inline node</a> other than a
+    <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, add one to <var title="">end offset</var>.
 
     <li>Otherwise, break from this loop.
   </ol>
--- a/implementation.js	Sun Jul 10 11:39:52 2011 -0600
+++ b/implementation.js	Sun Jul 10 12:08:36 2011 -0600
@@ -3680,21 +3680,31 @@
 			startOffset = getNodeIndex(startNode);
 			startNode = startNode.parentNode;
 
-		// "Otherwise, if start offset is start node's length and start node's
-		// last child is an inline node that's not a br, subtract one from
-		// start offset."
-		} else if (startOffset == getNodeLength(startNode)
-		&& isInlineNode(startNode.lastChild)
-		&& !isHtmlElement(startNode.lastChild, "br")) {
+		// "Otherwise, if start node's child with index start offset minus one
+		// is invisible, subtract one from start offset."
+		} else if (isInvisible(startNode.childNodes[startOffset - 1])) {
 			startOffset--;
 
-		// "Otherwise, if start node has a child with index start offset, and
-		// that child and its previousSibling are both inline nodes and the
-		// previousSibling isn't a br, subtract one from start offset."
-		} else if (startOffset < startNode.childNodes.length
-		&& isInlineNode(startNode.childNodes[startOffset])
-		&& isInlineNode(startNode.childNodes[startOffset].previousSibling)
-		&& !isHtmlElement(startNode.childNodes[startOffset].previousSibling, "BR")) {
+		// "Otherwise, if start node has no visible children with index greater
+		// than or equal to start offset and start node's last visible child is
+		// an inline node that's not a br, subtract one from start offset."
+		} else if (![].slice.call(startNode.childNodes, startOffset).some(isVisible)
+		&& isInlineNode([].filter.call(startNode.childNodes, isVisible).slice(-1)[0])
+		&& !isHtmlElement([].filter.call(startNode.childNodes, isVisible).slice(-1)[0], "br")) {
+			startOffset--;
+
+		// "Otherwise, if start node has a visible child with index greater
+		// than or equal to start offset, and the first such child is an inline
+		// node, and start node's child with index start offset minus one is an
+		// inline node other than a br, subtract one from start offset."
+		//
+		// The functional programming here might be a bit heavy, but it would
+		// be a pain to write it differently.
+		} else if (isInlineNode([].filter.call(startNode.childNodes, function(child, i) {
+			return isVisible(child) && i >= startOffset;
+		})[0])
+		&& isInlineNode(startNode.childNodes[startOffset - 1])
+		&& !isHtmlElement(startNode.childNodes[startOffset - 1], "br")) {
 			startOffset--;
 
 		// "Otherwise, break from this loop."
@@ -3728,20 +3738,28 @@
 			endOffset = 1 + getNodeIndex(endNode);
 			endNode = endNode.parentNode;
 
-		// "Otherwise, if end offset is 0 and end node's first child is an
-		// inline node that's not a br, add one to end offset."
-		} else if (endOffset == 0
-		&& isInlineNode(endNode.firstChild)
-		&& !isHtmlElement(endNode.firstChild, "br")) {
+		// "Otherwise, if end node's child with index end offset is invisible,
+		// add one to end offset."
+		} else if (isInvisible(endNode.childNodes[endOffset])) {
 			endOffset++;
 
-		// "Otherwise, if end node has a child with index end offset, and that
-		// child and its previousSibling are both inline nodes, and the
-		// previousSibling isn't a br, add one to end offset."
-		} else if (endOffset < endNode.childNodes.length
+		// "Otherwise, if end node has no visible children with index less than
+		// end offset and end node's first visible child is an inline node
+		// that's not a br, add one to end offset."
+		} else if (![].slice.call(endNode.childNodes, 0, endOffset).some(isVisible)
+		&& isInlineNode([].filter.call(endNode.childNodes, isVisible)[0])
+		&& !isHtmlElement([].filter.call(endNode.childNodes, isVisible)[0], "br")) {
+			endOffset++;
+
+		// "Otherwise, if end node has a visible child with index less than end
+		// offset, and the last such child is an inline node, and end node's
+		// child with index end offset is an inline node other than a br, add
+		// one to end offset."
+		} else if (isInlineNode([].filter.call(endNode.childNodes, function(child, i) {
+			return isVisible(child) && i < endOffset;
+		}).slice(-1)[0])
 		&& isInlineNode(endNode.childNodes[endOffset])
-		&& isInlineNode(endNode.childNodes[endOffset].previousSibling)
-		&& !isHtmlElement(endNode.childNodes[endOffset], "BR")) {
+		&& !isHtmlElement(endNode.childNodes[endOffset], "br")) {
 			endOffset++;
 
 		// "Otherwise, break from this loop."
--- a/source.html	Sun Jul 10 11:39:52 2011 -0600
+++ b/source.html	Sun Jul 10 12:08:36 2011 -0600
@@ -3250,24 +3250,27 @@
     of <var>start node</var> and then set <var>start node</var> to its
     [[parent]].
 
-    <li>Otherwise, if <var>start offset</var> is <var>start node</var>'s
-    [[nodelength]] and <var>start node</var>'s last [[child]] is an
-    <span>inline node</span> that's not a [[br]], subtract one from <var>start
-    offset</var>.
+    <li>Otherwise, if <var>start node</var>'s [[child]] with [[index]]
+    <var>start offset</var> minus one is <span>invisible</span>, subtract one
+    from <var>start offset</var>.
+
+    <li>Otherwise, if <var>start node</var> has no <span>visible</span>
+    [[children]] with [[index]] greater than or equal to <var>start
+    offset</var> and <var>start node</var>'s last <span>visible</span>
+    [[child]] is an <span>inline node</span> that's not a [[br]], subtract one
+    from <var>start offset</var>.
     <!-- So if you have a collapsed selection at the end of a block, for
     instance, it will extend backwards into a block. -->
 
-    <li>Otherwise, if <var>start node</var> has a [[child]] with [[index]]
-    <var>start offset</var>, and that [[child]] and its [[previoussibling]] are
-    both <span title="inline node">inline nodes</span> and the
-    [[previoussibling]] isn't a [[br]], subtract one from <var>start
-    offset</var>.
+    <li>Otherwise, if <var>start node</var> has a <span>visible</span>
+    [[child]] with [[index]] greater than or equal to <var>start offset</var>,
+    and the first such [[child]] is an <span>inline node</span>, and <var>start
+    node</var>'s [[child]] with [[index]] <var>start offset</var> minus one is
+    an <span>inline node</span> other than a [[br]], subtract one from
+    <var>start offset</var>.
     <!-- IE also includes <br> (at least for the purposes of the indent
     command), but this is unlikely to match user expectations. -->
 
-    <p class=XXX>This is wrong in the presence of comments or display: none or
-    probably other things.  Do we care?
-
     <li>Otherwise, break from this loop.
   </ol>
 
@@ -3283,15 +3286,19 @@
     <var>end offset</var> to one plus the [[index]] of <var>end node</var> and
     then set <var>end node</var> to its [[parent]].
 
-    <li>Otherwise, if <var>end offset</var> is 0 and <var>end node</var>'s
-    first [[child]] is an <span>inline node</span> that's not a [[br]], add one
-    to <var>end offset</var>.
-
-    <li>Otherwise, if <var>end node</var> has a [[child]] with [[index]]
-    <var>end offset</var>, and that [[child]] and its [[previoussibling]] are
-    both <span title="inline node">inline nodes</span>, and the [[child]] of
-    <var>end node</var> with [[index]] <var>end offset</var> isn't a [[br]],
-    add one to <var>end offset</var>.
+    <li>Otherwise, if <var>end node</var>'s [[child]] with [[index]] <var>end
+    offset</var> is <span>invisible</span>, add one to <var>end offset</var>.
+
+    <li>Otherwise, if <var>end node</var> has no <span>visible</span>
+    [[children]] with [[index]] less than <var>end offset</var> and <var>end
+    node</var>'s first <span>visible</span> [[child]] is an <span>inline
+    node</span> that's not a [[br]], add one to <var>end offset</var>.
+
+    <li>Otherwise, if <var>end node</var> has a <span>visible</span> [[child]]
+    with [[index]] less than <var>end offset</var>, and the last such [[child]]
+    is an <span>inline node</span>, and <var>end node</var>'s [[child]] with
+    [[index]] <var>end offset</var> is an <span>inline node</span> other than a
+    [[br]], add one to <var>end offset</var>.
 
     <li>Otherwise, break from this loop.
   </ol>
--- a/tests.js	Sun Jul 10 11:39:52 2011 -0600
+++ b/tests.js	Sun Jul 10 12:08:36 2011 -0600
@@ -1430,6 +1430,11 @@
 		'<ol><li>foo<ol><li>[bar]</ol>baz</ol>',
 		'<ol><li>foo<ol><li>bar</ol>[baz]</ol>',
 		'<ol><li>[foo<ol><li>bar]</ol>baz</ol>',
+
+		'foo<!--bar-->[baz]<p>extra',
+		'[foo]<!--bar-->baz<p>extra',
+		'<p>foo<!--bar-->{}<p>extra',
+		'<p>{}<!--foo-->bar<p>extra',
 	],
 	//@}
 	inserthorizontalrule: [