Initial forwardDelete spec/implementation
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 15 Jun 2011 14:35:26 -0600
changeset 276 8988db297760
parent 275 4978032e6927
child 277 2ec48d7f0147
Initial forwardDelete spec/implementation

Only tested in WebKit, since only WebKit supports forwardDelete as such.
Will add some manual tests shortly to test other browsers.
editcommands.html
implementation.js
preprocess
source.html
tests.js
--- a/editcommands.html	Wed Jun 15 13:33:45 2011 -0600
+++ b/editcommands.html	Wed Jun 15 14:35:26 2011 -0600
@@ -112,16 +112,17 @@
    <li><a href=#justifying-the-selection><span class=secno>7.7 </span>Justifying the selection</a></li>
    <li><a href=#the-delete-command><span class=secno>7.8 </span>The <code title="">delete</code> command</a></li>
    <li><a href=#the-formatblock-command><span class=secno>7.9 </span>The <code title="">formatBlock</code> command</a></li>
-   <li><a href=#the-indent-command><span class=secno>7.10 </span>The <code title="">indent</code> command</a></li>
-   <li><a href=#the-inserthorizontalrule-command><span class=secno>7.11 </span>The <code title="">insertHorizontalRule</code> command</a></li>
-   <li><a href=#the-insertorderedlist-command><span class=secno>7.12 </span>The <code title="">insertOrderedList</code> command</a></li>
-   <li><a href=#the-insertparagraph-command><span class=secno>7.13 </span>The <code title="">insertParagraph</code> command</a></li>
-   <li><a href=#the-insertunorderedlist-command><span class=secno>7.14 </span>The <code title="">insertUnorderedList</code> command</a></li>
-   <li><a href=#the-justifycenter-command><span class=secno>7.15 </span>The <code title="">justifyCenter</code> command</a></li>
-   <li><a href=#the-justifyfull-command><span class=secno>7.16 </span>The <code title="">justifyFull</code> command</a></li>
-   <li><a href=#the-justifyleft-command><span class=secno>7.17 </span>The <code title="">justifyLeft</code> command</a></li>
-   <li><a href=#the-justifyright-command><span class=secno>7.18 </span>The <code title="">justifyRight</code> command</a></li>
-   <li><a href=#the-outdent-command><span class=secno>7.19 </span>The <code title="">outdent</code> command</a></ol></li>
+   <li><a href=#the-forwarddelete-command><span class=secno>7.10 </span>The <code title="">forwardDelete</code> command</a></li>
+   <li><a href=#the-indent-command><span class=secno>7.11 </span>The <code title="">indent</code> command</a></li>
+   <li><a href=#the-inserthorizontalrule-command><span class=secno>7.12 </span>The <code title="">insertHorizontalRule</code> command</a></li>
+   <li><a href=#the-insertorderedlist-command><span class=secno>7.13 </span>The <code title="">insertOrderedList</code> command</a></li>
+   <li><a href=#the-insertparagraph-command><span class=secno>7.14 </span>The <code title="">insertParagraph</code> command</a></li>
+   <li><a href=#the-insertunorderedlist-command><span class=secno>7.15 </span>The <code title="">insertUnorderedList</code> command</a></li>
+   <li><a href=#the-justifycenter-command><span class=secno>7.16 </span>The <code title="">justifyCenter</code> command</a></li>
+   <li><a href=#the-justifyfull-command><span class=secno>7.17 </span>The <code title="">justifyFull</code> command</a></li>
+   <li><a href=#the-justifyleft-command><span class=secno>7.18 </span>The <code title="">justifyLeft</code> command</a></li>
+   <li><a href=#the-justifyright-command><span class=secno>7.19 </span>The <code title="">justifyRight</code> command</a></li>
+   <li><a href=#the-outdent-command><span class=secno>7.20 </span>The <code title="">outdent</code> command</a></ol></li>
  <li><a href=#miscellaneous-commands><span class=secno>8 </span>Miscellaneous commands</a>
   <ol>
    <li><a href=#the-selectall-command><span class=secno>8.1 </span>The <code title="">selectAll</code> command</a></li>
@@ -4824,7 +4825,153 @@
 </ol>
 
 
-<h3 id=the-indent-command><span class=secno>7.10 </span><dfn>The <code title="">indent</code> command</dfn></h3>
+<h3 id=the-forwarddelete-command><span class=secno>7.10 </span><dfn>The <code title="">forwardDelete</code> command</dfn></h3>
+
+<p><a href=#action>Action</a>:
+<!-- Copy-pasted from delete, see there for comments. -->
+
+<ol>
+  <li>If the <a href=#active-range>active range</a> is not <code class=external data-anolis-spec=domrange title=dom-Range-collapsed><a href=http://html5.org/specs/dom-range.html#dom-range-collapsed>collapsed</a></code>, <a href=#delete-the-contents>delete the contents</a>
+  of the <a href=#active-range>active range</a> and abort these steps.
+
+  <p class=XXX>Maybe we sometimes want to do the following even if it isn't
+  collapsed?  WebKit seems to do some normalization on the range before
+  deciding whether it's collapsed, and that sounds like a good idea.
+
+  <li>Let <var title="">node</var> and <var title="">offset</var> be the <a href=#active-range>active
+  range</a>'s <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=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a>.
+
+  <li>Repeat the following steps:
+
+  <ol>
+    <li>If <var title="">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="">node</var> and
+    <var title="">node</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-nextSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling>nextSibling</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-nextSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling>nextSibling</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> 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>.
+
+    <!-- We need these extra two steps in forwardDelete, relative to delete, to
+    skip over line breaks at the end of blocks. -->
+    <li>Otherwise, if <var title="">offset</var> is one less than <var title="">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="">node</var> is a <a href=#prohibited-paragraph-child>prohibited paragraph
+    child</a> whose 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 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="">offset</var>.
+
+    <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> + 1, and 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> of <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> 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 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> of <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> + 1 is a
+    <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, add one to <var title="">offset</var>.
+
+    <div class=XXX>
+    <p>These two steps don't correctly handle things like
+
+    </p><xmp><p>foo<span><br></span></p></xmp>
+
+    <p>For that matter, nor do most cases where we deal with line breaks like
+    this.  Should add a bunch of tests for this and change stuff so we pass.
+    </div>
+
+    <li>Otherwise, if <var title="">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="">node</var> and <var title="">node</var> is not 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 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="">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> 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 zero.
+
+    <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
+  <var title="">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>, 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>) 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> + 1)
+  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.
+
+  <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> 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>) 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> + 1) and abort these steps.
+
+  <!-- No special list-item behavior for forwardDelete. -->
+
+  <li>Let <var title="">end node</var> equal <var title="">node</var> and let <var title="">end
+  offset</var> equal <var title="">offset</var>.
+
+  <li>While <var title="">end 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="">end node</var>, set
+  <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>.
+
+  <p class=XXX>The node at index end offset might be an invisible node.
+
+  <!-- No special indentation element behavior for forwardDelete. -->
+
+  <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="">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> 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>, abort these steps.
+
+  <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="">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> 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>:
+
+  <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="">end node</var>, <var title="">end 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>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="">end node</var>, <var title="">end offset</var> + 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>Abort these steps.
+  </ol>
+
+  <li>If <var title="">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="">node</var>, 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> 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 <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>:
+  <!-- Note, any br will do here: a br immediately after a block is always
+  significant. -->
+
+  <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><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-end title=concept-range-end>end</a>
+    (<var title="">end node</var>, <var title="">end 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="">end
+    node</var>, <var title="">end offset</var> + 1).
+
+    <li>Abort these steps.
+  </ol>
+
+  <!-- No special list-item behavior for forwardDelete. -->
+
+  <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="">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> is an <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> or <code class=external data-anolis-spec=html title="the dt element"><a href=http://www.whatwg.org/html/#the-dt-element>dt</a></code> or <code class=external data-anolis-spec=html title="the dd element"><a href=http://www.whatwg.org/html/#the-dd-element>dd</a></code>, 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> is
+  also an <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> or <code class=external data-anolis-spec=html title="the dt element"><a href=http://www.whatwg.org/html/#the-dt-element>dt</a></code> or <code class=external data-anolis-spec=html title="the dd element"><a href=http://www.whatwg.org/html/#the-dd-element>dd</a></code>, 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-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>, then set <var title="">end offset</var> to zero,
+  then set <var title="">node</var> to <var title="">end 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>, then
+  set <var title="">offset</var> to <var title="">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>.
+
+  <!-- General block-merging case. -->
+  <li>Otherwise, while <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>, set <var title="">end 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> and set
+  <var title="">end offset</var> to zero.
+
+  <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="">node</var>, <var title="">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="">end node</var>,
+  <var title="">end offset</var>).
+</ol>
+
+
+<h3 id=the-indent-command><span class=secno>7.11 </span><dfn>The <code title="">indent</code> command</dfn></h3>
 
 <!--
 IE9: Outputs <blockquote style="margin-right: 0px" dir="ltr">, or when
@@ -4928,7 +5075,7 @@
 </ol>
 
 
-<h3 id=the-inserthorizontalrule-command><span class=secno>7.11 </span><dfn>The <code title="">insertHorizontalRule</code> command</dfn></h3>
+<h3 id=the-inserthorizontalrule-command><span class=secno>7.12 </span><dfn>The <code title="">insertHorizontalRule</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -4982,13 +5129,13 @@
 </ol>
 
 
-<h3 id=the-insertorderedlist-command><span class=secno>7.12 </span><dfn>The <code title="">insertOrderedList</code> command</dfn></h3>
+<h3 id=the-insertorderedlist-command><span class=secno>7.13 </span><dfn>The <code title="">insertOrderedList</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#toggle-lists>Toggle lists</a> with <var title="">tag name</var>
 "ol".
 
 
-<h3 id=the-insertparagraph-command><span class=secno>7.13 </span><dfn>The <code title="">insertParagraph</code> command</dfn></h3>
+<h3 id=the-insertparagraph-command><span class=secno>7.14 </span><dfn>The <code title="">insertParagraph</code> command</dfn></h3>
 
 <!--
 There are three major behaviors here.  Firefox 5.0a2 behaves identically to
@@ -5239,36 +5386,36 @@
 </ol>
 
 
-<h3 id=the-insertunorderedlist-command><span class=secno>7.14 </span><dfn>The <code title="">insertUnorderedList</code> command</dfn></h3>
+<h3 id=the-insertunorderedlist-command><span class=secno>7.15 </span><dfn>The <code title="">insertUnorderedList</code> command</dfn></h3>
 <p><a href=#action>Action</a>: <a href=#toggle-lists>Toggle lists</a> with <var title="">tag name</var>
 "ul".
 
 
-<h3 id=the-justifycenter-command><span class=secno>7.15 </span><dfn>The <code title="">justifyCenter</code> command</dfn></h3>
+<h3 id=the-justifycenter-command><span class=secno>7.16 </span><dfn>The <code title="">justifyCenter</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "center".
 
 
-<h3 id=the-justifyfull-command><span class=secno>7.16 </span><dfn>The <code title="">justifyFull</code> command</dfn></h3>
+<h3 id=the-justifyfull-command><span class=secno>7.17 </span><dfn>The <code title="">justifyFull</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "justify".
 
 
-<h3 id=the-justifyleft-command><span class=secno>7.17 </span><dfn>The <code title="">justifyLeft</code> command</dfn></h3>
+<h3 id=the-justifyleft-command><span class=secno>7.18 </span><dfn>The <code title="">justifyLeft</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "left".
 
 
-<h3 id=the-justifyright-command><span class=secno>7.18 </span><dfn>The <code title="">justifyRight</code> command</dfn></h3>
+<h3 id=the-justifyright-command><span class=secno>7.19 </span><dfn>The <code title="">justifyRight</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "right".
 
 
-<h3 id=the-outdent-command><span class=secno>7.19 </span><dfn>The <code title="">outdent</code> command</dfn></h3>
+<h3 id=the-outdent-command><span class=secno>7.20 </span><dfn>The <code title="">outdent</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
--- a/implementation.js	Wed Jun 15 13:33:45 2011 -0600
+++ b/implementation.js	Wed Jun 15 14:35:26 2011 -0600
@@ -5006,6 +5006,183 @@
 };
 //@}
 
+///// The forwardDelete command /////
+//@{
+commands["forwarddelete"] = {
+	action: function() {
+		// "If the active range is not collapsed, delete the contents of the
+		// active range and abort these steps."
+		if (!getActiveRange().collapsed) {
+			deleteContents(getActiveRange());
+			return;
+		}
+
+		// "Let node and offset be the active range's start node and offset."
+		var node = getActiveRange().startContainer;
+		var offset = getActiveRange().startOffset;
+
+		// "Repeat the following steps:"
+		while (true) {
+			// "If offset is the length of node and node's nextSibling is an
+			// editable invisible node, remove node's nextSibling from its
+			// parent."
+			if (offset == getNodeLength(node)
+			&& isEditable(node.nextSibling)
+			&& isInvisibleNode(node.nextSibling)) {
+				node.parentNode.removeChild(node.nextSibling);
+
+			// "Otherwise, if node has a child with index offset and that child
+			// is an editable invisible node, remove that child from node."
+			} else if (offset < node.childNodes.length
+			&& isEditable(node.childNodes[offset])
+			&& isInvisibleNode(node.childNodes[offset])) {
+				node.removeChild(node.childNodes[offset]);
+
+			// "Otherwise, if offset is one less than node's length, and node
+			// is a prohibited paragraph child whose last child is a br, add
+			// one to offset."
+			} else if (offset == getNodeLength(node) - 1
+			&& isProhibitedParagraphChild(node)
+			&& isHtmlElement(node.lastChild, "br")) {
+				offset++;
+
+			// "Otherwise, if node has a child with index offset + 1, and its
+			// child of index offset is a br, and its child of index offset + 1
+			// is a prohibited paragraph child, add one to offset."
+			} else if (offset + 1 < node.childNodes.length
+			&& isHtmlElement(node.childNodes[offset], "br")
+			&& isProhibitedParagraphChild(node.childNodes[offset + 1])) {
+				offset++;
+
+			// "Otherwise, if offset is the length of node and node is not a
+			// prohibited paragraph child, or if node is an invisible node, set
+			// offset to one plus the index of node, then set node to its
+			// parent."
+			} else if ((offset == getNodeLength(node)
+			&& !isProhibitedParagraphChild(node))
+			|| isInvisibleNode(node)) {
+				offset = 1 + getNodeIndex(node);
+				node = node.parentNode;
+
+			// "Otherwise, if node has a child with index offset and that child
+			// is not a prohibited paragraph child or a br or an img, set node
+			// to that child, then set offset to zero."
+			} else if (offset < node.childNodes.length
+			&& !isProhibitedParagraphChild(node.childNodes[offset])
+			&& !isHtmlElement(node.childNodes[offset], ["br", "img"])) {
+				node = node.childNodes[offset];
+				offset = 0;
+
+			// "Otherwise, break from this loop."
+			} else {
+				break;
+			}
+		}
+
+		// "If node is a Text node and offset is not node's length, call
+		// collapse(node, offset) on the Selection. Then delete the contents of
+		// the range with start (node, offset) and end (node, offset + 1) and
+		// abort these steps."
+		if (node.nodeType == Node.TEXT_NODE
+		&& offset != getNodeLength(node)) {
+			getActiveRange().setStart(node, offset);
+			getActiveRange().setEnd(node, offset);
+			deleteContents(node, offset, node, offset + 1);
+			return;
+		}
+
+		// "If node is not a prohibited paragraph child, abort these steps."
+		if (!isProhibitedParagraphChild(node)) {
+			return;
+		}
+
+		// "If node has a child with index offset 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) and end (node,
+		// offset + 1) and abort these steps."
+		if (offset < node.childNodes.length
+		&& isHtmlElement(node.childNodes[offset], ["br", "hr", "img"])) {
+			getActiveRange().setStart(node, offset);
+			getActiveRange().setEnd(node, offset);
+			deleteContents(node, offset, node, offset + 1);
+			return;
+		}
+
+		// "Let end node equal node and let end offset equal offset."
+		var endNode = node;
+		var endOffset = offset;
+
+		// "While end offset is the length of end node, set end offset to one
+		// plus the index of end node and then set end node to its parent."
+		while (endOffset == getNodeLength(endNode)) {
+			endOffset = 1 + getNodeIndex(endNode);
+			endNode = endNode.parentNode;
+		}
+
+		// "If the child of end node with index end offset minus one is a
+		// table, abort these steps."
+		if (isHtmlElement(endNode.childNodes[endOffset - 1], "table")) {
+			return;
+		}
+
+		// "If the child of end node with index end offset is a table:"
+		if (isHtmlElement(endNode.childNodes[endOffset], "table")) {
+			// "Call collapse(end node, end offset) on the context object's
+			// Selection."
+			getActiveRange().setStart(endNode, endOffset);
+
+			// "Call extend(end node, end offset + 1) on the context object's
+			// Selection."
+			getActiveRange().setEnd(endNode, endOffset + 1);
+
+			// "Abort these steps."
+			return;
+		}
+
+		// "If offset is the length of node, and the child of end node with
+		// index end offset is an hr or br:"
+		if (offset == getNodeLength(node)
+		&& isHtmlElement(endNode.childNodes[endOffset], ["br", "hr"])) {
+			// "Call collapse(node, offset) on the Selection."
+			getActiveRange().setStart(node, offset);
+			getActiveRange().setEnd(node, offset);
+
+			// "Delete the contents of the range with end (end node, end
+			// offset) and end (end node, end offset + 1)."
+			deleteContents(endNode, endOffset, endNode, endOffset + 1);
+
+			// "Abort these steps."
+			return;
+		}
+
+		// "If the child of end node with index end offset is an li or dt or
+		// dd, and its previousSibling is also an li or dt or dd, set end node
+		// to its child with index end offset, then set end offset to zero,
+		// then set node to end node's previousSibling, then set offset to
+		// node's length."
+		if (isHtmlElement(endNode.childNodes[endOffset], ["li", "dt", "dd"])
+		&& isHtmlElement(endNode.childNodes[endOffset - 1], ["li", "dt", "dd"])) {
+			endNode = endNode.childNodes[endOffset];
+			endOffset = 0;
+			node = endNode.previousSibling;
+			offset = getNodeLength(node);
+
+		// "Otherwise, while end node has a child with index end offset, set
+		// end node to that child and set end offset to zero."
+		} else {
+			while (endOffset < endNode.childNodes.length) {
+				endNode = endNode.childNodes[endOffset];
+				endOffset = 0;
+			}
+		}
+
+		// "Delete the contents of the range with start (node, offset) and end
+		// (end node, end offset)."
+		deleteContents(node, offset, endNode, endOffset);
+	}
+};
+//@}
+
 ///// The indent command /////
 //@{
 commands.indent = {
--- a/preprocess	Wed Jun 15 13:33:45 2011 -0600
+++ b/preprocess	Wed Jun 15 14:35:26 2011 -0600
@@ -54,6 +54,7 @@
     'img': '<code data-anolis-spec=html title="the img element">img</code>',
     'index': '<span data-anolis-spec=domrange title=concept-indexof>index</span>',
     'lastchild': '<code data-anolis-spec=domcore title=dom-Node-lastChild>lastChild</code>',
+    'length': '<span data-anolis-spec=domrange title=concept-node-length>length</span>',
     'li': '<code data-anolis-spec=html title="the li element">li</code>',
     'localname': '<span data-anolis-spec=domcore title=concept-element-local-name>local name</span>',
     'namespace': '<span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>',
--- a/source.html	Wed Jun 15 13:33:45 2011 -0600
+++ b/source.html	Wed Jun 15 14:35:26 2011 -0600
@@ -4831,6 +4831,153 @@
 </ol>
 <!-- @} -->
 
+<h3><dfn>The <code title>forwardDelete</code> command</dfn></h3>
+<!-- @{ -->
+<p><span>Action</span>:
+<!-- Copy-pasted from delete, see there for comments. -->
+
+<ol>
+  <li>If the <span>active range</span> is not <code data-anolis-spec=domrange
+  title=dom-Range-collapsed>collapsed</code>, <span>delete the contents</span>
+  of the <span>active range</span> and abort these steps.
+
+  <p class=XXX>Maybe we sometimes want to do the following even if it isn't
+  collapsed?  WebKit seems to do some normalization on the range before
+  deciding whether it's collapsed, and that sounds like a good idea.
+
+  <li>Let <var>node</var> and <var>offset</var> be the <span>active
+  range</span>'s [[rangestart]] [[bpnode]] and [[bpoffset]].
+
+  <li>Repeat the following steps:
+
+  <ol>
+    <li>If <var>offset</var> is the [[nodelength]] of <var>node</var> and
+    <var>node</var>'s [[nextsibling]] is an <span>editable</span>
+    <span>invisible node</span>, remove <var>node</var>'s [[nextsibling]] from
+    its [[parent]].
+
+    <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+    <var>offset</var> and that [[child]] is an <span>editable</span>
+    <span>invisible node</span>, remove that [[child]] from <var>node</var>.
+
+    <!-- We need these extra two steps in forwardDelete, relative to delete, to
+    skip over line breaks at the end of blocks. -->
+    <li>Otherwise, if <var>offset</var> is one less than <var>node</var>'s
+    [[length]], and <var>node</var> is a <span>prohibited paragraph
+    child</span> whose last [[child]] is a [[br]], add one to
+    <var>offset</var>.
+
+    <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+    <var>offset</var> + 1, and its [[child]] of [[index]] <var>offset</var> is
+    a [[br]], and its [[child]] of [[index]] <var>offset</var> + 1 is a
+    <span>prohibited paragraph child</span>, add one to <var>offset</var>.
+
+    <div class=XXX>
+    <p>These two steps don't correctly handle things like
+
+    <xmp><p>foo<span><br></span></p></xmp>
+
+    <p>For that matter, nor do most cases where we deal with line breaks like
+    this.  Should add a bunch of tests for this and change stuff so we pass.
+    </div>
+
+    <li>Otherwise, if <var>offset</var> is the [[nodelength]] of
+    <var>node</var> and <var>node</var> is not a <span>prohibited paragraph
+    child</span>, or if <var>node</var> is an <span>invisible node</span>, set
+    <var>offset</var> to one plus 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> 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 zero.
+
+    <li>Otherwise, break from this loop.
+  </ol>
+
+  <li>If <var>node</var> is a [[text]] node and <var>offset</var> is not
+  <var>node</var>'s [[nodelength]], 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>) and [[rangeend]] (<var>node</var>, <var>offset</var> + 1)
+  and abort these steps.
+
+  <li>If <var>node</var> is not a <span>prohibited paragraph child</span>,
+  abort these steps.
+
+  <li>If <var>node</var> has a [[child]] with [[index]] <var>offset</var> 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>) and [[rangeend]] (<var>node</var>,
+  <var>offset</var> + 1) and abort these steps.
+
+  <!-- No special list-item behavior for forwardDelete. -->
+
+  <li>Let <var>end node</var> equal <var>node</var> and let <var>end
+  offset</var> equal <var>offset</var>.
+
+  <li>While <var>end offset</var> is the [[length]] of <var>end node</var>, set
+  <var>end offset</var> to one plus the [[index]] of <var>end node</var> and
+  then set <var>end node</var> to its [[parent]].
+
+  <p class=XXX>The node at index end offset might be an invisible node.
+
+  <!-- No special indentation element behavior for forwardDelete. -->
+
+  <li>If the [[child]] of <var>end node</var> with [[index]] <var>end
+  offset</var> minus one is a [[table]], abort these steps.
+
+  <li>If the [[child]] of <var>end node</var> with [[index]] <var>end
+  offset</var> is a [[table]]:
+
+  <ol>
+    <li>Call [[selcollapse|<var>end node</var>, <var>end offset</var>]] on the
+    [[contextobject]]'s [[selection]].
+
+    <li>Call [[extend|<var>end node</var>, <var>end offset</var> + 1]] on the
+    [[contextobject]]'s [[selection]].
+
+    <li>Abort these steps.
+  </ol>
+
+  <li>If <var>offset</var> is the [[length]] of <var>node</var>, and the
+  [[child]] of <var>end node</var> with [[index]] <var>end offset</var> is an
+  [[hr]] or [[br]]:
+  <!-- Note, any br will do here: a br immediately after a block is always
+  significant. -->
+
+  <ol>
+    <li>Call [[selcollapse|<var>node</var>, <var>offset</var>]] on the
+    [[selection]].
+
+    <li><span>Delete the contents</span> of the [[range]] with [[rangeend]]
+    (<var>end node</var>, <var>end offset</var>) and [[rangeend]] (<var>end
+    node</var>, <var>end offset</var> + 1).
+
+    <li>Abort these steps.
+  </ol>
+
+  <!-- No special list-item behavior for forwardDelete. -->
+
+  <li>If the [[child]] of <var>end node</var> with [[index]] <var>end
+  offset</var> is an [[li]] or [[dt]] or [[dd]], and its [[previoussibling]] is
+  also an [[li]] or [[dt]] or [[dd]], set <var>end node</var> to its [[child]]
+  with [[index]] <var>end offset</var>, then set <var>end offset</var> to zero,
+  then set <var>node</var> to <var>end node</var>'s [[previoussibling]], then
+  set <var>offset</var> to <var>node</var>'s [[length]].
+
+  <!-- General block-merging case. -->
+  <li>Otherwise, while <var>end node</var> has a [[child]] with [[index]]
+  <var>end offset</var>, set <var>end node</var> to that [[child]] and set
+  <var>end offset</var> to zero.
+
+  <li><span>Delete the contents</span> of the [[range]] with [[rangestart]]
+  (<var>node</var>, <var>offset</var>) and [[rangeend]] (<var>end node</var>,
+  <var>end offset</var>).
+</ol>
+<!-- @} -->
+
 <h3><dfn>The <code title>indent</code> command</dfn></h3>
 <!-- @{ -->
 <!--
--- a/tests.js	Wed Jun 15 13:33:45 2011 -0600
+++ b/tests.js	Wed Jun 15 14:35:26 2011 -0600
@@ -870,6 +870,210 @@
 		['<p>', '<xmp>[foo]</xmp>'],
 		['<div>', '<xmp>[foo]</xmp>'],
 	],
+	forwarddelete: [
+		// Collapsed selection
+		//
+		// These three commented-out test call Firefox 5.0a2 to blow up, not
+		// just throwing exceptions on the tests themselves but on many
+		// subsequent tests too.
+		'foo[]',
+		'<span>foo[]</span>',
+		'<p>foo[]</p>',
+		'foo[]bar',
+		'<span>foo</span>{}<span>bar</span>',
+		'<span>foo[</span><span>]bar</span>',
+		'foo[]<span style=display:none>bar</span>baz',
+		'fo[]&ouml;bar',
+		'fo[]o&#x308;bar',
+
+		'<p>foo[]</p><p>bar</p>',
+		'<p>foo[]</p>bar',
+		'foo[]<p>bar</p>',
+		'<p>foo[]<br></p><p>bar</p>',
+		'<p>foo[]<br></p>bar',
+		'foo[]<br><p>bar</p>',
+
+		'<div><p>foo[]</p></div><p>bar</p>',
+		'<p>foo[]</p><div><p>bar</p></div>',
+		'<div><p>foo[]</p></div><div><p>bar</p></div>',
+		'<div><p>foo[]</p></div>bar',
+		'foo[]<div><p>bar</p></div>',
+
+		'<div>foo[]</div><div>bar</div>',
+		'<pre>foo[]</pre>bar',
+
+		'foo[]<br>bar',
+		'<b>foo[]</b><br>bar',
+		'foo[]<hr>bar',
+		'<p>foo[]<hr><p>bar',
+		'<p>foo[]</p><br><p>bar</p>',
+		'<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',
+		'<a href=/>foo[]</a>bar',
+		'foo[]<a href=/>bar</a>',
+
+		// Tables with collapsed selection
+		'foo[]<table><tr><td>bar</table>baz',
+		'foo<table><tr><td>bar[]</table>baz',
+		'<p>foo[]<table><tr><td>bar</table><p>baz',
+		'<p>foo[]<table><tr><td>bar</table><p>baz',
+		'<table><tr><td>foo[]<td>bar</table>',
+		'<table><tr><td>foo[]<tr><td>bar</table>',
+
+		'foo[]<br><table><tr><td>bar</table>baz',
+		'foo<table><tr><td>bar[]<br></table>baz',
+		'<p>foo[]<br><table><tr><td>bar</table><p>baz',
+		'<p>foo<table><tr><td>bar[]<br></table><p>baz',
+		'<table><tr><td>foo[]<br><td>bar</table>',
+		'<table><tr><td>foo[]<br><tr><td>bar</table>',
+
+		'foo<table><tr><td>bar[]</table><br>baz',
+		'foo[]<table><tr><td><hr>bar</table>baz',
+		'<table><tr><td>foo[]<td><hr>bar</table>',
+		'<table><tr><td>foo[]<tr><td><hr>bar</table>',
+
+		// Lists with collapsed selection
+		'foo[]<ol><li>bar<li>baz</ol>',
+		'foo[]<br><ol><li>bar<li>baz</ol>',
+		'<ol><li>foo[]<li>bar</ol>',
+		'<ol><li>foo[]<br><li>bar</ol>',
+		'<ol><li>foo[]<li>bar<br>baz</ol>',
+
+		'<ol><li><p>foo[]<li>bar</ol>',
+		'<ol><li>foo[]<li><p>bar</ol>',
+		'<ol><li><p>foo[]<li><p>bar</ol>',
+
+		'<ol><li>foo[]<ul><li>bar</ul></ol>',
+		'foo[]<ol><ol><li>bar</ol></ol>',
+		'foo[]<div><ol><li>bar</ol></div>',
+
+		'foo[]<dl><dt>bar<dd>baz</dl>',
+		'foo[]<dl><dd>bar</dl>',
+		'<dl><dt>foo[]<dd>bar</dl>',
+		'<dl><dt>foo[]<dt>bar<dd>baz</dl>',
+		'<dl><dt>foo<dd>bar[]<dd>baz</dl>',
+
+		'<ol><li>foo[]</ol>bar',
+		'<ol><li>foo[]<br></ol>bar',
+
+		'<ol><li>{}<br></ol>bar',
+		'<ol><li>foo<li>{}<br></ol>bar',
+
+		// Indented stuff with collapsed selection
+		'foo[]<blockquote>bar</blockquote>',
+		'foo[]<blockquote><blockquote>bar</blockquote></blockquote>',
+		'foo[]<blockquote><div>bar</div></blockquote>',
+		'foo[]<blockquote style="color: red">bar</blockquote>',
+
+		'foo[]<blockquote><blockquote><p>bar<p>baz</blockquote></blockquote>',
+		'foo[]<blockquote><div><p>bar<p>baz</div></blockquote>',
+		'foo[]<blockquote style="color: red"><p>bar<p>baz</blockquote>',
+
+		'foo[]<blockquote><p><b>bar</b><p>baz</blockquote>',
+		'foo[]<blockquote><p><strong>bar</strong><p>baz</blockquote>',
+		'foo[]<blockquote><p><span>bar</span><p>baz</blockquote>',
+
+		'foo[]<blockquote><ol><li>bar</ol></blockquote><p>extra',
+		'foo[]<blockquote>bar<ol><li>baz</ol>quz</blockquote><p>extra',
+		'foo<blockquote><ol><li>bar[]</li><ol><li>baz</ol><li>quz</ol></blockquote><p>extra',
+
+		// Invisible stuff with collapsed selection
+		'foo[]<span></span>bar',
+		'foo[]<span><span></span></span>bar',
+		'foo[]<quasit></quasit>bar',
+		'foo[]<span></span><br>bar',
+		'<span>foo[]<span></span></span>bar',
+		'foo[]<span></span><span>bar</span>',
+
+		// Uncollapsed selection (should be same as delete command)
+		'foo[bar]baz',
+
+		'foo<b>[bar]</b>baz',
+		'foo<b>{bar}</b>baz',
+		'foo{<b>bar</b>}baz',
+		'foo<span>[bar]</span>baz',
+		'foo<span>{bar}</span>baz',
+		'foo{<span>bar</span>}baz',
+		'<b>foo[bar</b><i>baz]quz</i>',
+		'<p>foo</p><p>[bar]</p><p>baz</p>',
+		'<p>foo</p><p>{bar}</p><p>baz</p>',
+		'<p>foo</p><p>{bar</p>}<p>baz</p>',
+		'<p>foo</p>{<p>bar}</p><p>baz</p>',
+		'<p>foo</p>{<p>bar</p>}<p>baz</p>',
+
+		'<p>foo[bar<p>baz]quz',
+		'<p>foo[bar<div>baz]quz</div>',
+		'<p>foo[bar<h1>baz]quz</h1>',
+		'<div>foo[bar</div><p>baz]quz',
+		'<blockquote>foo[bar</blockquote><pre>baz]quz</pre>',
+
+		'<p><b>foo[bar</b><p>baz]quz',
+		'<div><p>foo[bar</div><p>baz]quz',
+		'<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote',
+		'<p>foo[bar<p style=color:red>baz]quz',
+		'<p>foo[bar<p><b>baz]quz</b>',
+
+		'<div><p>foo<p>[bar<p>baz]</div>',
+
+		'foo[<br>]bar',
+		'<p>foo[</p><p>]bar</p>',
+		'<p>foo[</p><p>]bar<br>baz</p>',
+		'foo[<p>]bar</p>',
+		'foo{<p>}bar</p>',
+		'foo[<p>]bar<br>baz</p>',
+		'foo[<p>]bar</p>baz',
+		'foo{<p>bar</p>}baz',
+		'foo<p>{bar</p>}baz',
+		'foo{<p>bar}</p>baz',
+		'<p>foo[</p>]bar',
+		'<p>foo{</p>}bar',
+		'<p>foo[</p>]bar<br>baz',
+		'<p>foo[</p>]bar<p>baz</p>',
+		'foo[<div><p>]bar</div>',
+		'<div><p>foo[</p></div>]bar',
+		'foo[<div><p>]bar</p>baz</div>',
+		'foo[<div>]bar<p>baz</p></div>',
+		'<div><p>foo</p>bar[</div>]baz',
+		'<div>foo<p>bar[</p></div>]baz',
+
+		'<p>foo<br>{</p>]bar',
+		'<p>foo<br><br>{</p>]bar',
+		'foo<br>{<p>]bar</p>',
+		'foo<br><br>{<p>]bar</p>',
+		'<p>foo<br>{</p><p>}bar</p>',
+		'<p>foo<br><br>{</p><p>}bar</p>',
+
+		'<table><tbody><tr><th>foo<th>[bar]<th>baz<tr><td>quz<td>qoz<td>qiz</table>',
+		'<table><tbody><tr><th>foo<th>ba[r<th>b]az<tr><td>quz<td>qoz<td>qiz</table>',
+		'<table><tbody><tr><th>fo[o<th>bar<th>b]az<tr><td>quz<td>qoz<td>qiz</table>',
+		'<table><tbody><tr><th>foo<th>bar<th>ba[z<tr><td>q]uz<td>qoz<td>qiz</table>',
+		'<table><tbody><tr><th>[foo<th>bar<th>baz]<tr><td>quz<td>qoz<td>qiz</table>',
+		'<table><tbody><tr><th>[foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz]</table>',
+		'{<table><tbody><tr><th>foo<th>bar<th>baz<tr><td>quz<td>qoz<td>qiz</table>}',
+		'<table><tbody><tr><td>foo<td>ba[r<tr><td>baz<td>quz<tr><td>q]oz<td>qiz</table>',
+		'<p>fo[o<table><tr><td>b]ar</table><p>baz',
+		'<p>foo<table><tr><td>ba[r</table><p>b]az',
+		'<p>fo[o<table><tr><td>bar</table><p>b]az',
+
+		'<p>foo<ol><li>ba[r<li>b]az</ol><p>quz',
+		'<p>foo<ol><li>bar<li>[baz]</ol><p>quz',
+		'<p>fo[o<ol><li>b]ar<li>baz</ol><p>quz',
+		'<p>foo<ol><li>bar<li>ba[z</ol><p>q]uz',
+		'<p>fo[o<ol><li>bar<li>b]az</ol><p>quz',
+		'<p>fo[o<ol><li>bar<li>baz</ol><p>q]uz',
+
+		'<ol><li>fo[o</ol><ol><li>b]ar</ol>',
+		'<ol><li>fo[o</ol><ul><li>b]ar</ul>',
+
+		'foo[<ol><li>]bar</ol>',
+		'<ol><li>foo[<li>]bar</ol>',
+		'foo[<dl><dt>]bar<dd>baz</dl>',
+		'foo[<dl><dd>]bar</dl>',
+		'<dl><dt>foo[<dd>]bar</dl>',
+		'<dl><dt>foo[<dt>]bar<dd>baz</dl>',
+		'<dl><dt>foo<dd>bar[<dd>]baz</dl>',
+	],
 	hilitecolor: [
 		'foo[]bar',
 		'<span>foo</span>{}<span>bar</span>',