Refactor delete command for simplicity
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Sun, 12 Jun 2011 13:21:09 -0600
changeset 256 8fb704c6f76d
parent 255 fa08ade96842
child 257 0fa57bc91eea
Refactor delete command for simplicity
editcommands.html
implementation.js
source.html
--- a/editcommands.html	Sun Jun 12 12:24:28 2011 -0600
+++ b/editcommands.html	Sun Jun 12 13:21:09 2011 -0600
@@ -2515,8 +2515,9 @@
     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>.
+    <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, or if <var title="">node</var> is an
+    <a href=#invisible-node>invisible node</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
@@ -2527,12 +2528,33 @@
     <li>Otherwise, break from this loop.
   </ol>
 
+  <!--
+  At this point, node cannot be an invisible node.  There are three cases:
+
+  1) offset is zero and node is a prohibited paragraph child.  Then we'll
+  usually merge with the previous block if one exists.
+
+  2) offset is not zero, node is not a prohibited paragraph child, and node
+  does not have a child with index offset - 1.  The only way this is possible
+  is if node has a length greater than zero but no children, which implies it's
+  a text or comment or PI.  Comments and PIs are invisible nodes, so it must be
+  a text node.  We delete the previous character.
+
+  3) offset is not zero, and the child of node with index offset - 1 is a
+  prohibited paragraph child or a br or an img.  Then we'll usually merge the
+  offsetth child of node with the last descendant of the offset - 1st.
+  -->
+
   <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>.
   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 not a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>,
+  abort these steps.
+  <!-- At the time of this writing, this should be impossible. -->
+
   <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 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>.
@@ -2540,127 +2562,81 @@
   (<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:
-  <!-- Then we'll typically want to merge backward into the previous block. -->
-
-  <ol>
-    <li>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>.
-
-    <li>Let <var title="">start node</var> equal <var title="">node</var> and let <var title="">start
-    offset</var> equal <var title="">offset</var>.
-
-    <li>While <var title="">start offset</var> is zero, set <var title="">start 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="">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>.
-
-    <!--
-    This is to avoid stripping a line break
-
-      foo<br><br><table><tr><td>[]bar</table>
-
-    and similarly for <hr>.  We should just do nothing here.
-    -->
-    <li>If 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="">start 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="">start
-    offset</var> is a <code class=external data-anolis-spec=html title="the table element"><a href=http://www.whatwg.org/html/#the-table-element>table</a></code>, abort these steps.
-
-    <!--
-    Special case:
-
-      <p>foo</p><br><p>[]bar</p>
-      -> <p>foo</p><p>[]bar</p>
-
-    and likewise for <hr>.  But with <img> we merge like in other cases:
-
-      <p>foo</p><img><p>[]bar</p>
-      -> <p>foo</p><img>[]bar.
-
-    Browsers don't do this consistently.  Firefox 5.0a2 doesn't seem to do it
-    at all.
-    -->
-    <li>If 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="">start 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="">start
-    offset</var> minus one 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>, or 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> 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> and the
-    <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>'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 either 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 not an <a href=#inline-node>inline
-    node</a>, set <var title="">node</var> to <var title="">start node</var> and
-    <var title="">offset</var> to <var title="">start offset</var>, then subtract one from
-    <var title="">start offset</var>.
-
-    <!--
-    Another special case:
-
-      <table>...</table><p>[]foo</p>
-      -> {<table>...</table>}<p>foo</p>
-
-    same as happens with <table>...</table>[]foo (see below).
-    -->
-    <li>Otherwise, if 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="">start 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="">start offset</var> minus one is a <code class=external data-anolis-spec=html title="the table element"><a href=http://www.whatwg.org/html/#the-table-element>table</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="">start node</var>, <var title="">start offset</var> &minus; 1)</a></code> on
-    the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>, then call <code class=external data-anolis-spec=domrange title=dom-Selection-extend><a href=http://html5.org/specs/dom-range.html#dom-selection-extend>extend(<var title="">start
-    node</var>, <var title="">start offset</var>)</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s
-    <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>, then abort these steps.
-
-    <!--
-    Regular case: for <p>foo</p><p>[]bar</p>, we delete <p>foo{</p><p>}bar</p>.
-    This will also happen if the preceding element is a span or something, but
-    that doesn't hurt.
-    -->
-    <li>Otherwise, 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-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, then set <var title="">start 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="">start node</var>.
-
-    <li>While <var title="">start node</var> is a <a href=#prohibited-paragraph-child>prohibited paragraph
-    child</a> whose <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> is a <a href=#prohibited-paragraph-child>prohibited paragraph
-    child</a>, and <var title="">start offset</var> is 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="">start node</var>, set <var title="">start node</var> to its <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> and
-    then set <var title="">start 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="">start
-    node</var>.
-
-    <li><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="">start node</var>, <var title="">start offset</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>
-    (<var title="">node</var>, <var title="">offset</var>).
-
-    <li>Abort these steps.
-  </ol>
-
-  <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 a <code class=external data-anolis-spec=html title="the table element"><a href=http://www.whatwg.org/html/#the-table-element>table</a></code>:
+  <!-- By this point, we're almost certainly going to merge something, and the
+  only question is what. -->
+  <li>Let <var title="">start node</var> equal <var title="">node</var> and let <var title="">start
+  offset</var> equal <var title="">offset</var>.
+
+  <li>While <var title="">start offset</var> is zero, set <var title="">start 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="">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>.
+
+  <!--
+  This is to avoid stripping a line break from
+
+    foo<br><br><table><tr><td>[]bar</table>
+
+  and similarly for <hr>.  We should just do nothing here.
+  -->
+  <li>If 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="">start 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="">start
+  offset</var> is a <code class=external data-anolis-spec=html title="the table element"><a href=http://www.whatwg.org/html/#the-table-element>table</a></code>, abort these steps.
+
+  <li>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> &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 table element"><a href=http://www.whatwg.org/html/#the-table-element>table</a></code>:
   <!-- If you try backspacing into a table, select it.  This doesn't match any
   browser; it matches the recommendation of the "behavior when typing in
   contentEditable elements" document.  The idea is that then you can delete it
   with a second backspace. -->
 
   <ol>
-    <li>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> &minus; 1)</a></code> on
-    the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
-
-    <li>Call <code class=external data-anolis-spec=domrange title=dom-Selection-extend><a href=http://html5.org/specs/dom-range.html#dom-selection-extend>extend(<var title="">node</var>, <var title="">offset</var>)</a></code> on the
+    <li>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="">start node</var>, <var title="">start offset</var>
+    &minus; 1)</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
+
+    <li>Call <code class=external data-anolis-spec=domrange title=dom-Selection-extend><a href=http://html5.org/specs/dom-range.html#dom-selection-extend>extend(<var title="">start node</var>, <var title="">start offset</var>)</a></code> on the
     <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <code class=external data-anolis-spec=domrange><a href=http://html5.org/specs/dom-range.html#selection>Selection</a></code>.
 
     <li>Abort these steps.
   </ol>
 
-  <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 a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>:
-
+  <li>If <var title="">offset</var> is zero; and either 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="">start
+  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="">start offset</var> minus one 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>, or
+  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> 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> whose <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 either 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 not
+  an <a href=#inline-node>inline node</a>:
+
+  <!--
+  Special case:
+
+    <p>foo</p><br><p>[]bar</p>
+    -> <p>foo</p><p>[]bar</p>
+
+  and likewise for <hr>.  But with <img> we merge like in other cases:
+
+    <p>foo</p><img><p>[]bar</p>
+    -> <p>foo</p><img>[]bar.
+
+  Browsers don't do this consistently.  Firefox 5.0a2 doesn't seem to do it at
+  all.
+  -->
   <ol>
-    <li>Let <var title="">start node</var> be 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="">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="">offset</var> &minus; 1.
-
-    <li>While <var title="">start node</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> is a <a href=#prohibited-paragraph-child>prohibited
-    paragraph child</a>, set <var title="">start node</var> to its <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code>.
-
-    <li>Let <var title="">start offset</var> be 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="">start
-    node</var>.
+    <li>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>.
 
     <li><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="">start node</var>, <var title="">start offset</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>
-    (<var title="">node</var>, <var title="">offset</var>).
+    (<var title="">start node</var>, <var title="">start 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="">start node</var>, <var title="">start offset</var>).
 
     <li>Abort these steps.
   </ol>
 
+  <!-- General block-merging case. -->
+  <li>While <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> minus one, set <var title="">start 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="">start 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="">start node</var>.
+
+  <li><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="">start node</var>, <var title="">start offset</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>
+  (<var title="">node</var>, <var title="">offset</var>).
+
   <li class=XXX>Lots more cases to handle.
 </ol>
 
--- a/implementation.js	Sun Jun 12 12:24:28 2011 -0600
+++ b/implementation.js	Sun Jun 12 13:21:09 2011 -0600
@@ -3226,10 +3226,11 @@
 				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)) {
+			// paragraph child, or if node is an invisible node, set offset to
+			// the index of node, then set node to its parent."
+			} else if ((offset == 0
+			&& !isProhibitedParagraphChild(node))
+			|| isInvisibleNode(node)) {
 				offset = getNodeIndex(node);
 				node = node.parentNode;
 
@@ -3261,6 +3262,11 @@
 			return;
 		}
 
+		// "If node is not a prohibited paragraph child, abort these steps."
+		if (!isProhibitedParagraphChild(node)) {
+			return;
+		}
+
 		// "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
@@ -3274,122 +3280,77 @@
 			return;
 		}
 
-		// "If node is a prohibited paragraph child and offset is zero:"
-		if (isProhibitedParagraphChild(node)
-		&& offset == 0) {
-			// "Call collapse(node, offset) on the Selection."
-			range.setStart(node, offset);
-			range.setEnd(node, offset);
-
-			// "Let start node equal node and let start offset equal offset."
-			var startNode = node;
-			var startOffset = offset;
-
-			// "While start offset is zero, set start offset to the index of
-			// start node and then set start node to its parent."
-			while (startOffset == 0) {
-				startOffset = getNodeIndex(startNode);
-				startNode = startNode.parentNode;
-			}
-
-			// "If the child of start node with index start offset is a table,
-			// abort these steps."
-			if (isHtmlElement(startNode.childNodes[startOffset], "table")) {
-				return;
-			}
-
-			// "If the child of start node with index start offset minus one is
-			// an hr, or the child is a br and the br's previousSibling is
-			// either a br or not an inline node, set node to start node and
-			// offset to start offset, then subtract one from start offset."
-			if (isHtmlElement(startNode.childNodes[startOffset - 1], "hr")
+		// "Let start node equal node and let start offset equal offset."
+		var startNode = node;
+		var startOffset = offset;
+
+		// "While start offset is zero, set start offset to the index of start
+		// node and then set start node to its parent."
+		while (startOffset == 0) {
+			startOffset = getNodeIndex(startNode);
+			startNode = startNode.parentNode;
+		}
+
+		// "If the child of start node with index start offset is a table,
+		// abort these steps."
+		if (isHtmlElement(startNode.childNodes[startOffset], "table")) {
+			return;
+		}
+
+		// "If start node has a child with index start offset − 1, and that
+		// child is a table:"
+		if (0 <= startOffset - 1
+		&& startOffset - 1 < startNode.childNodes.length
+		&& isHtmlElement(startNode.childNodes[startOffset - 1], "table")) {
+			// "Call collapse(start node, start offset − 1) on the context
+			// object's Selection."
+			range.setStart(startNode, startOffset - 1);
+
+			// "Call extend(start node, start offset) on the context object's
+			// Selection."
+			range.setEnd(startNode, startOffset);
+
+			// "Abort these steps."
+			return;
+		}
+
+		// "If offset is zero; and either the child of start node with index
+		// start offset minus one is an hr, or the child is a br whose
+		// previousSibling is either a br or not an inline node:"
+		if (offset == 0
+		&& (isHtmlElement(startNode.childNodes[startOffset - 1], "hr")
 			|| (
 				isHtmlElement(startNode.childNodes[startOffset - 1], "br")
 				&& (
 					isHtmlElement(startNode.childNodes[startOffset - 1].previousSibling, "br")
 					|| !isInlineNode(startNode.childNodes[startOffset - 1].previousSibling)
 				)
-			)) {
-				node = startNode;
-				offset = startOffset;
-				startOffset--;
-
-			// "Otherwise, if the child of start node with index start offset
-			// minus one is a table, call collapse(start node, start offset −
-			// 1) on the context object's Selection, then call extend(start
-			// node, start offset) on the context object's Selection, then
-			// abort these steps."
-			} else if (isHtmlElement(startNode.childNodes[startOffset - 1], "table")) {
-				range.setStart(startNode, startOffset - 1);
-				range.setEnd(startNode, startOffset);
-				return;
-
-			// "Otherwise, set start node to its child with index start offset
-			// minus one, then set start offset to the length of start node."
-			} else {
-				startNode = startNode.childNodes[startOffset - 1];
-				startOffset = getNodeLength(startNode);
-			}
-
-			// "While start node is a prohibited paragraph child whose
-			// lastChild is a prohibited paragraph child, and start offset is
-			// the length of start node, set start node to its lastChild and
-			// then set start offset to the length of start node."
-			while (isProhibitedParagraphChild(startNode)
-			&& isProhibitedParagraphChild(startNode.lastChild)
-			&& startOffset == getNodeLength(startNode)) {
-				startNode = startNode.lastChild;
-				startOffset = getNodeLength(startNode);
-			}
+			)
+		)) {
+			// "Call collapse(node, offset) on the Selection."
+			range.setStart(node, offset);
+			range.setEnd(node, offset);
 
 			// "Delete the contents of the range with start (start node, start
-			// offset) and end (node, offset)."
-			deleteContents(startNode, startOffset, node, offset);
+			// offset − 1) and end (start node, start offset)."
+			deleteContents(startNode, startOffset - 1, startNode, startOffset);
 
 			// "Abort these steps."
 			return;
 		}
 
-		// "If node has a child with index offset − 1, and that child is a
-		// table:"
-		if (0 <= offset - 1
-		&& offset - 1 < node.childNodes.length
-		&& isHtmlElement(node.childNodes[offset - 1], "table")) {
-			// "Call collapse(node, offset − 1) on the context object's
-			// Selection."
-			range.setStart(node, offset - 1);
-
-			// "Call extend(node, offset) on the context object's Selection."
-			range.setEnd(node, offset);
-
-			// "Abort these steps."
-			return;
-		}
-
-		// "If node has a child with index offset − 1, and that child is a
-		// prohibited paragraph child:"
-		if (0 <= offset - 1
-		&& offset - 1 < node.childNodes.length
-		&& isProhibitedParagraphChild(node.childNodes[offset - 1])) {
-			// "Let start node be the child of node with index offset − 1."
-			var startNode = node.childNodes[offset - 1];
-
-			// "While start node's lastChild is a prohibited paragraph child,
-			// set start node to its lastChild."
-			while (isProhibitedParagraphChild(startNode.lastChild)) {
-				startNode = startNode.lastChild;
-			}
-
-			// "Let start offset be the length of start node."
-			var startOffset = getNodeLength(startNode);
-
-			// "Delete the contents of the range with start (start node, start
-			// offset) and end (node, offset)."
-			deleteContents(startNode, startOffset, node, offset);
-
-			// "Abort these steps."
-			return;
-		}
+		// "While start node has a child with index start offset minus one, set
+		// start node to that child, then set start offset to the length of
+		// start node."
+		while (0 <= startOffset - 1
+		&& startOffset - 1 < startNode.childNodes.length) {
+			startNode = startNode.childNodes[startOffset - 1];
+			startOffset = getNodeLength(startNode);
+		}
+
+		// "Delete the contents of the range with start (start node, start
+		// offset) and end (node, offset)."
+		deleteContents(startNode, startOffset, node, offset);
 		break;
 
 		case "fontname":
--- a/source.html	Sun Jun 12 12:24:28 2011 -0600
+++ b/source.html	Sun Jun 12 13:21:09 2011 -0600
@@ -2495,8 +2495,9 @@
     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]].
+    <span>prohibited paragraph child</span>, or if <var>node</var> is an
+    <span>invisible node</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
@@ -2507,12 +2508,33 @@
     <li>Otherwise, break from this loop.
   </ol>
 
+  <!--
+  At this point, node cannot be an invisible node.  There are three cases:
+
+  1) offset is zero and node is a prohibited paragraph child.  Then we'll
+  usually merge with the previous block if one exists.
+
+  2) offset is not zero, node is not a prohibited paragraph child, and node
+  does not have a child with index offset - 1.  The only way this is possible
+  is if node has a length greater than zero but no children, which implies it's
+  a text or comment or PI.  Comments and PIs are invisible nodes, so it must be
+  a text node.  We delete the previous character.
+
+  3) offset is not zero, and the child of node with index offset - 1 is a
+  prohibited paragraph child or a br or an img.  Then we'll usually merge the
+  offsetth child of node with the last descendant of the offset - 1st.
+  -->
+
   <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]].
   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 not a <span>prohibited paragraph child</span>,
+  abort these steps.
+  <!-- At the time of this writing, this should be impossible. -->
+
   <li>If <var>node</var> has a [[child]] with [[index]] <var>offset</var>
   &minus; 1 and that [[child]] is a [[br]] or [[hr]] or [[img]], call
   [[selcollapse|<var>node</var>, <var>offset</var>]] on the [[selection]].
@@ -2520,127 +2542,81 @@
   (<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:
-  <!-- Then we'll typically want to merge backward into the previous block. -->
-
-  <ol>
-    <li>Call [[selcollapse|<var>node</var>, <var>offset</var>]] on the
-    [[selection]].
-
-    <li>Let <var>start node</var> equal <var>node</var> and let <var>start
-    offset</var> equal <var>offset</var>.
-
-    <li>While <var>start offset</var> is zero, set <var>start offset</var> to
-    the [[index]] of <var>start node</var> and then set <var>start node</var>
-    to its [[parent]].
-
-    <!--
-    This is to avoid stripping a line break
-
-      foo<br><br><table><tr><td>[]bar</table>
-
-    and similarly for <hr>.  We should just do nothing here.
-    -->
-    <li>If the [[child]] of <var>start node</var> with [[index]] <var>start
-    offset</var> is a [[table]], abort these steps.
-
-    <!--
-    Special case:
-
-      <p>foo</p><br><p>[]bar</p>
-      -> <p>foo</p><p>[]bar</p>
-
-    and likewise for <hr>.  But with <img> we merge like in other cases:
-
-      <p>foo</p><img><p>[]bar</p>
-      -> <p>foo</p><img>[]bar.
-
-    Browsers don't do this consistently.  Firefox 5.0a2 doesn't seem to do it
-    at all.
-    -->
-    <li>If the [[child]] of <var>start node</var> with [[index]] <var>start
-    offset</var> minus one is an [[hr]], or the [[child]] is a [[br]] and the
-    [[br]]'s [[previoussibling]] is either a [[br]] or not an <span>inline
-    node</span>, set <var>node</var> to <var>start node</var> and
-    <var>offset</var> to <var>start offset</var>, then subtract one from
-    <var>start offset</var>.
-
-    <!--
-    Another special case:
-
-      <table>...</table><p>[]foo</p>
-      -> {<table>...</table>}<p>foo</p>
-
-    same as happens with <table>...</table>[]foo (see below).
-    -->
-    <li>Otherwise, if the [[child]] of <var>start node</var> with [[index]]
-    <var>start offset</var> minus one is a [[table]], call
-    [[selcollapse|<var>start node</var>, <var>start offset</var> &minus; 1]] on
-    the [[contextobject]]'s [[selection]], then call [[extend|<var>start
-    node</var>, <var>start offset</var>]] on the [[contextobject]]'s
-    [[selection]], then abort these steps.
-
-    <!--
-    Regular case: for <p>foo</p><p>[]bar</p>, we delete <p>foo{</p><p>}bar</p>.
-    This will also happen if the preceding element is a span or something, but
-    that doesn't hurt.
-    -->
-    <li>Otherwise, set <var>start node</var> to its [[child]] with [[index]]
-    <var>start offset</var> minus one, then set <var>start offset</var> to the
-    [[nodelength]] of <var>start node</var>.
-
-    <li>While <var>start node</var> is a <span>prohibited paragraph
-    child</span> whose [[lastchild]] is a <span>prohibited paragraph
-    child</span>, and <var>start offset</var> is the [[nodelength]] of
-    <var>start node</var>, set <var>start node</var> to its [[lastchild]] and
-    then set <var>start offset</var> to the [[nodelength]] of <var>start
-    node</var>.
-
-    <li><span>Delete the contents</span> of the [[range]] with [[rangestart]]
-    (<var>start node</var>, <var>start offset</var>) and [[rangeend]]
-    (<var>node</var>, <var>offset</var>).
-
-    <li>Abort these steps.
-  </ol>
-
-  <li>If <var>node</var> has a [[child]] with [[index]] <var>offset</var>
-  &minus; 1, and that [[child]] is a [[table]]:
+  <!-- By this point, we're almost certainly going to merge something, and the
+  only question is what. -->
+  <li>Let <var>start node</var> equal <var>node</var> and let <var>start
+  offset</var> equal <var>offset</var>.
+
+  <li>While <var>start offset</var> is zero, set <var>start offset</var> to the
+  [[index]] of <var>start node</var> and then set <var>start node</var> to its
+  [[parent]].
+
+  <!--
+  This is to avoid stripping a line break from
+
+    foo<br><br><table><tr><td>[]bar</table>
+
+  and similarly for <hr>.  We should just do nothing here.
+  -->
+  <li>If the [[child]] of <var>start node</var> with [[index]] <var>start
+  offset</var> is a [[table]], abort these steps.
+
+  <li>If <var>start node</var> has a [[child]] with [[index]] <var>start
+  offset</var> &minus; 1, and that [[child]] is a [[table]]:
   <!-- If you try backspacing into a table, select it.  This doesn't match any
   browser; it matches the recommendation of the "behavior when typing in
   contentEditable elements" document.  The idea is that then you can delete it
   with a second backspace. -->
 
   <ol>
-    <li>Call [[selcollapse|<var>node</var>, <var>offset</var> &minus; 1]] on
-    the [[contextobject]]'s [[selection]].
-
-    <li>Call [[extend|<var>node</var>, <var>offset</var>]] on the
+    <li>Call [[selcollapse|<var>start node</var>, <var>start offset</var>
+    &minus; 1]] on the [[contextobject]]'s [[selection]].
+
+    <li>Call [[extend|<var>start node</var>, <var>start offset</var>]] on the
     [[contextobject]]'s [[selection]].
 
     <li>Abort these steps.
   </ol>
 
-  <li>If <var>node</var> has a [[child]] with [[index]] <var>offset</var>
-  &minus; 1, and that [[child]] is a <span>prohibited paragraph child</span>:
-
+  <li>If <var>offset</var> is zero; and either the [[child]] of <var>start
+  node</var> with [[index]] <var>start offset</var> minus one is an [[hr]], or
+  the [[child]] is a [[br]] whose [[previoussibling]] is either a [[br]] or not
+  an <span>inline node</span>:
+
+  <!--
+  Special case:
+
+    <p>foo</p><br><p>[]bar</p>
+    -> <p>foo</p><p>[]bar</p>
+
+  and likewise for <hr>.  But with <img> we merge like in other cases:
+
+    <p>foo</p><img><p>[]bar</p>
+    -> <p>foo</p><img>[]bar.
+
+  Browsers don't do this consistently.  Firefox 5.0a2 doesn't seem to do it at
+  all.
+  -->
   <ol>
-    <li>Let <var>start node</var> be the [[child]] of <var>node</var> with
-    [[index]] <var>offset</var> &minus; 1.
-
-    <li>While <var>start node</var>'s [[lastchild]] is a <span>prohibited
-    paragraph child</span>, set <var>start node</var> to its [[lastchild]].
-
-    <li>Let <var>start offset</var> be the [[nodelength]] of <var>start
-    node</var>.
+    <li>Call [[selcollapse|<var>node</var>, <var>offset</var>]] on the
+    [[selection]].
 
     <li><span>Delete the contents</span> of the [[range]] with [[rangestart]]
-    (<var>start node</var>, <var>start offset</var>) and [[rangeend]]
-    (<var>node</var>, <var>offset</var>).
+    (<var>start node</var>, <var>start offset</var> &minus; 1) and [[rangeend]]
+    (<var>start node</var>, <var>start offset</var>).
 
     <li>Abort these steps.
   </ol>
 
+  <!-- General block-merging case. -->
+  <li>While <var>start node</var> has a [[child]] with [[index]] <var>start
+  offset</var> minus one, set <var>start node</var> to that [[child]], then set
+  <var>start offset</var> to the [[nodelength]] of <var>start node</var>.
+
+  <li><span>Delete the contents</span> of the [[range]] with [[rangestart]]
+  (<var>start node</var>, <var>start offset</var>) and [[rangeend]]
+  (<var>node</var>, <var>offset</var>).
+
   <li class=XXX>Lots more cases to handle.
 </ol>