Try to handle invisible things while deleting
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Tue, 07 Jun 2011 13:25:30 -0600
changeset 248 31f2368d02c9
parent 247 8b15f8fcc5c6
child 249 6a1817dbf0eb
Try to handle invisible things while deleting

Pretty crude for now, but could be easily expanded to include display:
none, collapsed whitespace nodes, and other things in the future.
editcommands.html
implementation.js
source.html
tests.js
--- a/editcommands.html	Tue Jun 07 12:59:08 2011 -0600
+++ b/editcommands.html	Tue Jun 07 13:25:30 2011 -0600
@@ -401,6 +401,16 @@
 I think.  Plus table stuff, since that can't be a descendant of a p either,
 although it won't auto-close it. -->
 
+<p>A <dfn id=visible-node>visible node</dfn> is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that either is a <a href=#prohibited-paragraph-child>prohibited
+paragraph child</a>, or a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node whose <code class=external data-anolis-spec=domcore title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> is not empty, or 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> or <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, or any <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> with a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendant</a> that is a
+<a href=#visible-node>visible node</a>.  An <dfn id=invisible-node>invisible node</dfn> is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is
+not a <a href=#visible-node>visible node</a>.
+
+<p class=XXX>I don't know if this definition is really the way we want to do
+things.  If it is, it needs some adjustment, like to handle collapsed
+whitespace nodes and collapsed br's.
+
 <p>A <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> or string <var title="">child</var> is an <dfn id=allowed-child>allowed child</dfn> of a
 <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> or string <var title="">parent</var> if the following algorithm returns true:
 
@@ -2478,14 +2488,38 @@
   <!-- First go up as high as possible within the current block, then drill
   down to the lowest possible level, in the hopes that we'll wind up at the end
   of a text node, or maybe in a br or hr. -->
-  <li>While <var title="">offset</var> is zero and <var title="">node</var> is not a
-  <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, set <var title="">offset</var> to 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="">node</var>, then set <var title="">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>While <var title="">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="">offset</var>
-  &minus; 1 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> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
-  child</a>, set <var title="">node</var> to 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>, then set
-  <var title="">offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">node</var>.
+  <li>Repeat the following steps:
+
+  <ol>
+    <!--
+    If there's an invisible node somewhere, Firefox 5.0a2 removes that node and
+    then stops, so each backspace removes one invisible node.  All others
+    remove the invisible node and then continue on looking for something
+    visible to remove.  The spec follows the latter behavior, since it makes
+    more sense to the user.  Of course, the definition of "invisible node" is
+    not necessarily anything like the spec's.
+    -->
+    <li>If <var title="">offset</var> is zero and <var title="">node</var>'s <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>
+    is an <a href=#editable>editable</a> <a href=#invisible-node>invisible node</a>, remove
+    <var title="">node</var>'s <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> from 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="">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="">offset</var> &minus; 1 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> is an <a href=#editable>editable</a>
+    <a href=#invisible-node>invisible node</a>, remove 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> from <var title="">node</var>,
+    then subtract one from <var title="">offset</var>.
+
+    <li>Otherwise, if <var title="">offset</var> is zero and <var title="">node</var> is not a
+    <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, set <var title="">offset</var> to 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="">node</var>, then set <var title="">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="">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="">offset</var> &minus; 1 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> is not a <a href=#prohibited-paragraph-child>prohibited
+    paragraph child</a> or 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> or an <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, set <var title="">node</var> to
+    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>, then set <var title="">offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of
+    <var title="">node</var>.
+
+    <li>Otherwise, break from this loop.
+  </ol>
 
   <li>If <var title="">node</var> is a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node and <var title="">offset</var> is not zero,
   call <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse(<var title="">node</var>, <var title="">offset</var>)</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
@@ -2494,14 +2528,11 @@
   (<var title="">node</var>, <var title="">offset</var>) and abort these steps.
 
   <li>If <var title="">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="">offset</var>
-  &minus; 1 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> is an <code class=external data-anolis-spec=html title="the hr element"><a href=http://www.whatwg.org/html/#the-hr-element>hr</a></code>, set <var title="">node</var> to 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>.
-
-  <li>If <var title="">node</var> is 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> or <code class=external data-anolis-spec=html title="the hr element"><a href=http://www.whatwg.org/html/#the-hr-element>hr</a></code> or <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, call
-  <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse(<var title="">node</var>, 0)</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.  Then <a href=#delete-the-contents>delete
-  the contents</a> of the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> (<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> of
-  <var title="">node</var>, <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="">node</var>) and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> (<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>
-  of <var title="">node</var>, 1 + <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="">node</var>) and abort these steps.
+  &minus; 1 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> is 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> or <code class=external data-anolis-spec=html title="the hr element"><a href=http://www.whatwg.org/html/#the-hr-element>hr</a></code> or <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, call
+  <code class=external data-anolis-spec=domrange title=dom-Selection-collapse><a href=http://html5.org/specs/dom-range.html#dom-selection-collapse>collapse(<var title="">node</var>, <var title="">offset</var>)</a></code> on the <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
+  Then <a href=#delete-the-contents>delete the contents</a> of the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range title=concept-range>range</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
+  (<var title="">node</var>, <var title="">offset</var> &minus; 1) and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
+  (<var title="">node</var>, <var title="">offset</var>) and abort these steps.
 
   <li>If <var title="">node</var> is a <a href=#prohibited-paragraph-child>prohibited paragraph child</a> and
   <var title="">offset</var> is zero:
--- a/implementation.js	Tue Jun 07 12:59:08 2011 -0600
+++ b/implementation.js	Tue Jun 07 13:25:30 2011 -0600
@@ -1295,6 +1295,31 @@
 	return isHtmlElement(node, prohibitedParagraphChildNames);
 }
 
+// "A visible node is a node that either is a prohibited paragraph child, or a
+// Text node whose data is not empty, or a br or img, or any node with a
+// descendant that is a visible node."
+function isVisibleNode(node) {
+	if (!node) {
+		return false;
+	}
+	if (isProhibitedParagraphChild(node)
+	|| (node.nodeType == Node.TEXT_NODE && node.length)
+	|| isHtmlElement(node, ["br", "img"])) {
+		return true;
+	}
+	for (var i = 0; i < node.childNodes.length; i++) {
+		if (isVisibleNode(node.childNodes[i])) {
+			return true;
+		}
+	}
+	return false;
+}
+
+// "An invisible node is a node that is not a visible node."
+function isInvisibleNode(node) {
+	return node && !isVisibleNode(node);
+}
+
 function isAllowedChild(child, parent_) {
 	// "If parent is "colgroup", "table", "tbody", "tfoot", "thead", "tr", or
 	// an HTML element with local name equal to one of those, and child is a
@@ -3167,22 +3192,47 @@
 		var node = range.startContainer;
 		var offset = range.startOffset;
 
-		// "While offset is zero and node is not a prohibited paragraph child,
-		// set offset to the index of node, then set node to its parent."
-		while (offset == 0
-		&& !isProhibitedParagraphChild(node)) {
-			offset = getNodeIndex(node);
-			node = node.parentNode;
-		}
-
-		// "While node has a child with index offset − 1 and that child is not
-		// a prohibited paragraph child, set node to that child, then set
-		// offset to the length of node."
-		while (0 <= offset - 1
-		&& offset - 1 < node.childNodes.length
-		&& !isProhibitedParagraphChild(node.childNodes[offset - 1])) {
-			node = node.childNodes[offset - 1];
-			offset = getNodeLength(node);
+		// "Repeat the following steps:"
+		while (true) {
+			// "If offset is zero and node's previousSibling is an editable
+			// invisible node, remove node's previousSibling from its parent."
+			if (offset == 0
+			&& isEditable(node.previousSibling)
+			&& isInvisibleNode(node.previousSibling)) {
+				node.parentNode.removeChild(node.previousSibling);
+
+			// "Otherwise, if node has a child with index offset − 1 and that
+			// child is an editable invisible node, remove that child from
+			// node, then subtract one from offset."
+			} else if (0 <= offset - 1
+			&& offset - 1 < node.childNodes.length
+			&& isEditable(node.childNodes[offset - 1])
+			&& isInvisibleNode(node.childNodes[offset - 1])) {
+				node.removeChild(node.childNodes[offset - 1]);
+				offset--;
+
+			// "Otherwise, if offset is zero and node is not a prohibited
+			// paragraph child, set offset to the index of node, then set node
+			// to its parent."
+			} else if (offset == 0
+			&& !isProhibitedParagraphChild(node)) {
+				offset = getNodeIndex(node);
+				node = node.parentNode;
+
+			// "Otherwise, if node has a child with index offset − 1 and that
+			// child is not a prohibited paragraph child or a br or an img, set
+			// node to that child, then set offset to the length of node."
+			} else if (0 <= offset - 1
+			&& offset - 1 < node.childNodes.length
+			&& !isProhibitedParagraphChild(node.childNodes[offset - 1])
+			&& !isHtmlElement(node.childNodes[offset - 1], ["br", "img"])) {
+				node = node.childNodes[offset - 1];
+				offset = getNodeLength(node);
+
+			// "Otherwise, break from this loop."
+			} else {
+				break;
+			}
 		}
 
 		// "If node is a Text node and offset is not zero, call collapse(node,
@@ -3197,22 +3247,16 @@
 			return;
 		}
 
-		// "If node has a child with index offset − 1 and that child is an hr,
-		// set node to that child."
-		if (0 <= offset -1
+		// "If node has a child with index offset − 1 and that child is a br or
+		// hr or img, call collapse(node, offset) on the Selection. Then delete
+		// the contents of the range with start (node, offset − 1) and end
+		// (node, offset) and abort these steps."
+		if (0 <= offset - 1
 		&& offset - 1 < node.childNodes.length
-		&& isHtmlElement(node.childNodes[offset - 1], "hr")) {
-			node = node.childNodes[offset - 1];
-		}
-
-		// "If node is a br or hr or img, call collapse(node, 0) on the
-		// Selection.  Then delete the contents of the range with start (parent
-		// of node, index of node) and end (parent of node, 1 + index of node)
-		// and abort these steps."
-		if (isHtmlElement(node, ["br", "hr", "img"])) {
-			range.setStart(node, 0);
-			range.setEnd(node, 0);
-			deleteContents(node.parentNode, getNodeIndex(node), node.parentNode, 1 + getNodeIndex(node));
+		&& isHtmlElement(node.childNodes[offset - 1], ["br", "hr", "img"])) {
+			range.setStart(node, offset);
+			range.setEnd(node, offset);
+			deleteContents(node, offset - 1, node, offset);
 			return;
 		}
 
--- a/source.html	Tue Jun 07 12:59:08 2011 -0600
+++ b/source.html	Tue Jun 07 13:25:30 2011 -0600
@@ -345,6 +345,16 @@
 I think.  Plus table stuff, since that can't be a descendant of a p either,
 although it won't auto-close it. -->
 
+<p>A <dfn>visible node</dfn> is a [[node]] that either is a <span>prohibited
+paragraph child</span>, or a [[text]] node whose [[cddata]] is not empty, or a
+[[br]] or [[img]], or any [[node]] with a [[descendant]] that is a
+<span>visible node</span>.  An <dfn>invisible node</dfn> is a [[node]] that is
+not a <span>visible node</span>.
+
+<p class=XXX>I don't know if this definition is really the way we want to do
+things.  If it is, it needs some adjustment, like to handle collapsed
+whitespace nodes and collapsed br's.
+
 <p>A [[node]] or string <var>child</var> is an <dfn>allowed child</dfn> of a
 [[node]] or string <var>parent</var> if the following algorithm returns true:
 
@@ -2459,14 +2469,38 @@
   <!-- First go up as high as possible within the current block, then drill
   down to the lowest possible level, in the hopes that we'll wind up at the end
   of a text node, or maybe in a br or hr. -->
-  <li>While <var>offset</var> is zero and <var>node</var> is not a
-  <span>prohibited paragraph child</span>, set <var>offset</var> to the
-  [[index]] of <var>node</var>, then set <var>node</var> to its [[parent]].
-
-  <li>While <var>node</var> has a [[child]] with [[index]] <var>offset</var>
-  &minus; 1 and that [[child]] is not a <span>prohibited paragraph
-  child</span>, set <var>node</var> to that [[child]], then set
-  <var>offset</var> to the [[nodelength]] of <var>node</var>.
+  <li>Repeat the following steps:
+
+  <ol>
+    <!--
+    If there's an invisible node somewhere, Firefox 5.0a2 removes that node and
+    then stops, so each backspace removes one invisible node.  All others
+    remove the invisible node and then continue on looking for something
+    visible to remove.  The spec follows the latter behavior, since it makes
+    more sense to the user.  Of course, the definition of "invisible node" is
+    not necessarily anything like the spec's.
+    -->
+    <li>If <var>offset</var> is zero and <var>node</var>'s [[previoussibling]]
+    is an <span>editable</span> <span>invisible node</span>, remove
+    <var>node</var>'s [[previoussibling]] from its [[parent]].
+
+    <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+    <var>offset</var> &minus; 1 and that [[child]] is an <span>editable</span>
+    <span>invisible node</span>, remove that [[child]] from <var>node</var>,
+    then subtract one from <var>offset</var>.
+
+    <li>Otherwise, if <var>offset</var> is zero and <var>node</var> is not a
+    <span>prohibited paragraph child</span>, set <var>offset</var> to the
+    [[index]] of <var>node</var>, then set <var>node</var> to its [[parent]].
+
+    <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+    <var>offset</var> &minus; 1 and that [[child]] is not a <span>prohibited
+    paragraph child</span> or a [[br]] or an [[img]], set <var>node</var> to
+    that [[child]], then set <var>offset</var> to the [[nodelength]] of
+    <var>node</var>.
+
+    <li>Otherwise, break from this loop.
+  </ol>
 
   <li>If <var>node</var> is a [[text]] node and <var>offset</var> is not zero,
   call [[selcollapse|<var>node</var>, <var>offset</var>]] on the [[selection]].
@@ -2475,14 +2509,11 @@
   (<var>node</var>, <var>offset</var>) and abort these steps.
 
   <li>If <var>node</var> has a [[child]] with [[index]] <var>offset</var>
-  &minus; 1 and that [[child]] is an [[hr]], set <var>node</var> to that
-  [[child]].
-
-  <li>If <var>node</var> is a [[br]] or [[hr]] or [[img]], call
-  [[selcollapse|<var>node</var>, 0]] on the [[selection]].  Then <span>delete
-  the contents</span> of the [[range]] with [[rangestart]] ([[parent]] of
-  <var>node</var>, [[index]] of <var>node</var>) and [[rangeend]] ([[parent]]
-  of <var>node</var>, 1 + [[index]] of <var>node</var>) and abort these steps.
+  &minus; 1 and that [[child]] is a [[br]] or [[hr]] or [[img]], call
+  [[selcollapse|<var>node</var>, <var>offset</var>]] on the [[selection]].
+  Then <span>delete the contents</span> of the [[range]] with [[rangestart]]
+  (<var>node</var>, <var>offset</var> &minus; 1) and [[rangeend]]
+  (<var>node</var>, <var>offset</var>) and abort these steps.
 
   <li>If <var>node</var> is a <span>prohibited paragraph child</span> and
   <var>offset</var> is zero:
--- a/tests.js	Tue Jun 07 12:59:08 2011 -0600
+++ b/tests.js	Tue Jun 07 13:25:30 2011 -0600
@@ -250,9 +250,14 @@
 		'<p>foo</p><br><br><p>[]bar</p>',
 		'<p>foo</p><img src=/img/lion.svg><p>[]bar',
 		'foo<img src=/img/lion.svg>[]bar',
+
+		// Invisible stuff
 		'foo<span></span>[]bar',
 		'foo<span><span></span></span>[]bar',
 		'foo<quasit></quasit>[]bar',
+		'foo<br><span></span>[]bar',
+		'<span>foo<span></span></span>[]bar',
+		'foo<span></span><span>[]bar</span>',
 
 		'foo[bar]baz',