Improve outdent list support
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Sun, 08 May 2011 15:17:33 -0600
changeset 98 5ff17073b64e
parent 97 32d1bf688781
child 99 6f63f6aa573a
Improve outdent list support
editcommands.html
implementation.js
source.html
--- a/editcommands.html	Sun May 08 13:54:16 2011 -0600
+++ b/editcommands.html	Sun May 08 15:17:33 2011 -0600
@@ -1980,7 +1980,7 @@
   the result.
 
   <!-- If the first line box in a block box starts with a non-preserved line
-  break, the line break will create an empty line box.  If the line line box in
+  break, the line break will create an empty line box.  If the last line box in
   a block box ends with a non-preserved line break, however, it will not create
   an extra line box.  This means that if you have foo<br>bar, there will be no
   empty line in between, but if you change it to <div>foo</div><br>bar, you've
@@ -2581,7 +2581,27 @@
     <var title="">node list</var>.
   </ol>
 
-  <li><a href=#outdent>Outdent</a> each member of <var title="">node list</var>.
+  <li>While <var title="">node list</var> is not empty:
+
+  <ol>
+    <li>While the first member of <var title="">node list</var> is an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>
+    or is not 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 an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, <a href=#outdent>outdent</a> it and
+    remove it from <var title="">node list</var>.
+
+    <li>If <var title="">node list</var> is empty, break from these substeps.
+
+    <li>Let <var title="">sublist</var> be a list of <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a>, initially empty.
+
+    <li>Remove the first member of <var title="">node list</var> and append it to
+    <var title="">sublist</var>.
+
+    <li>While the first member of <var title="">node list</var> is the <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>
+    of the last member of <var title="">sublist</var>, and the first member of <var title="">node
+    list</var> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, remove the first member of <var title="">node
+    list</var> and append it to <var title="">sublist</var>.
+
+    <li><a href=#list-outdent>List-outdent</a> <var title="">sublist</var>.
+  </ol>
 </ol>
 
 <p>To <dfn id=outdent>outdent</dfn> 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> <var title="">node</var>:
@@ -2608,8 +2628,6 @@
 
   <p class=XXX>Handle this better for nested editable/non-editable.
 
-  <!-- First we handle the list case, because it's simpler. -->
-
   <li>If <var title="">node</var> is an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code> with no attributes except
   possibly <code class=external data-anolis-spec=html title=attr-ol-reversed><a href=http://www.whatwg.org/html/#attr-ol-reversed>reversed</a></code>,
   <code class=external data-anolis-spec=html title=attr-ol-start><a href=http://www.whatwg.org/html/#attr-ol-start>start</a></code>, and/or <code class=external data-anolis-spec=html title=attr-ol-type><a href=http://www.whatwg.org/html/#attr-ol-type>type</a></code>; or is an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or
@@ -2666,49 +2684,6 @@
     <li>Abort these steps.
   </ol>
 
-  <li>If <var title="">node</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> whose <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> is an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>:
-
-  <ol>
-    <li>Let <var title="">parent</var> be the <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>.
-
-    <li>If <var title="">parent</var>'s <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> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, unset the
-    <code class=external data-anolis-spec=html title=attr-li-value><a href=http://www.whatwg.org/html/#attr-li-value>value</a></code> attribute of
-    <var title="">node</var> if set, then <a href=#set-the-tag-name>set the tag name</a> of
-    <var title="">node</var> to "div" and set <var title="">node</var> to the result.
-
-    <li>If <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 null, insert
-    <var title="">node</var> into the <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="">parent</var> immediately before
-    <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>, then abort these steps.
-
-    <li>If <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 null, insert <var title="">node</var>
-    into the <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="">parent</var> immediately after
-    <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>, then abort these steps.
-
-    <!-- Otherwise, have to split up the list. -->
-
-    <li>Let <var title="">new parent</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Node-cloneNode><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-clonenode>cloneNode(false)</a></code>
-    on <var title="">parent</var>.
-
-    <p class=XXX>This will duplicate id's, as well as other bad things.  Do we
-    care?  We don't want to not copy attributes at all, because that wouldn't
-    copy style attributes, and Firefox in CSS mode will actually add style
-    attributes to any elements it feels like.  If we do exclude id's, even
-    though they'd only occur if someone manually added them, do we want to
-    exclude other things like itemid or accesskey or . . .
-
-    <li>Insert <var title="">new parent</var> into the <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="">parent</var>
-    immediately after <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
-
-    <li>While <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 not null, insert the 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> of <var title="">parent</var> as the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">new
-    parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
-
-    <li>Insert <var title="">node</var> into the <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="">parent</var>
-    immediately after <var title="">parent</var>.
-
-    <li>Abort these steps.
-  </ol>
-
   <!-- The easy case is when the whole element is indented.  In this case we
   remove the whole thing indiscriminately.  In the case of blockquotes
   created by IE, this might change the direction of some children, but then
@@ -2843,6 +2818,113 @@
   <li><a href=#outdent>Outdent</a> <var title="">original ancestor</var>.
 </ol>
 
+<p>To <dfn id=list-outdent>list-outdent</dfn> a list <var title="">node list</var> of consecutive
+<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-sibling title=concept-tree-sibling>sibling</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>nodes</a>:
+
+<ol>
+  <li>Let <var title="">parent</var> be the <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 the first member of <var title="">node
+  list</var>.
+
+  <li>If the <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> of the first member of <var title="">node list</var> is
+  null:
+
+  <ol>
+    <li>For each <var title="">node</var> in <var title="">node list</var>:
+
+    <ol>
+      <li>If <var title="">node</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>, unset its <code class=external data-anolis-spec=html title=attr-li-value><a href=http://www.whatwg.org/html/#attr-li-value>value</a></code> attribute if set.
+
+      <li>If <var title="">node</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> with no attributes, and the <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="">parent</var> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>:
+
+      <ol>
+        <li>If the <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> of <var title="">parent</var> and the first
+        <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">node</var> are both <a href=#inline-node title="inline node">inline
+        nodes</a>, let <var title="">br</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on the
+        <code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">node</var>, then insert <var title="">br</var> into
+        the <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="">parent</var> immediately before
+        <var title="">parent</var>.
+
+        <li>While <var title="">node</var> has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, insert the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of
+        <var title="">node</var> into the <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="">parent</var> immediately
+        before <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
+
+        <li>Remove <var title="">node</var> 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>.
+      </ol>
+
+      <li>Otherwise:
+
+      <ol>
+        <li>Insert <var title="">node</var> into the <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="">parent</var>
+        immediately before <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
+
+        <li>If <var title="">node</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> and the <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="">parent</var> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, <a href=#set-the-tag-name>set the tag
+        name</a> of <var title="">node</var> to <code class=external data-anolis-spec=html title="the div element"><a href=http://www.whatwg.org/html/#the-div-element>div</a></code>.
+      </ol>
+    </ol>
+
+    <li>Abort these steps.
+  </ol>
+
+  <li>If the <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> of the last member of <var title="">node list</var> is not
+  null:
+
+  <ol>
+    <li>Let <var title="">new parent</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Node-cloneNode><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-clonenode>cloneNode(false)</a></code>
+    on <var title="">parent</var>.
+
+    <p class=XXX>This will duplicate id's, as well as other bad things.  Do we
+    care?  We don't want to not copy attributes at all, because that wouldn't
+    copy style attributes, and Firefox in CSS mode will actually add style
+    attributes to any elements it feels like.  If we do exclude id's, even
+    though they'd only occur if someone manually added them, do we want to
+    exclude other things like itemid or accesskey or . . .
+
+    <li>Insert <var title="">new parent</var> into the <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="">parent</var>
+    immediately after <var title="">parent</var>.
+
+    <li>While <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> of the last member of <var title="">node list</var> is not
+    null, insert the 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> of <var title="">parent</var> as the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>
+    of <var title="">new parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
+  </ol>
+
+  <li>For each <var title="">node</var> in <var title="">node list</var>, <em>in reverse
+  order</em>:
+
+  <ol>
+    <li>If <var title="">node</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>, unset its <code class=external data-anolis-spec=html title=attr-li-value><a href=http://www.whatwg.org/html/#attr-li-value>value</a></code> attribute if set.
+
+    <li>If <var title="">node</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> with no attributes, and the <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="">parent</var> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>:
+
+    <ol>
+      <li>If the 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> of <var title="">node</var> and the <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> of
+      <var title="">parent</var> are both <a href=#inline-node title="inline node">inline nodes</a>,
+      let <var title="">br</var> be the result of calling <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on the
+      <code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">node</var>, then insert <var title="">br</var> into the
+      <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="">parent</var> immediately after <var title="">parent</var>.
+
+      <li>While <var title="">node</var> has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, insert the 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> of
+      <var title="">node</var> into the <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="">parent</var> immediately
+      after <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
+
+      <li>Remove <var title="">node</var> 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>.
+    </ol>
+
+    <li>Otherwise:
+
+    <ol>
+      <li>Insert <var title="">node</var> into the <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="">parent</var>
+      immediately after <var title="">parent</var>, <a href=#preserving-ranges>preserving ranges</a>.
+
+      <li>If <var title="">node</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> and the <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="">parent</var> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, <a href=#set-the-tag-name>set the tag
+      name</a> of <var title="">node</var> to <code class=external data-anolis-spec=html title="the div element"><a href=http://www.whatwg.org/html/#the-div-element>div</a></code>.
+    </ol>
+  </ol>
+</ol>
+
 <p>A <dfn id=potential-indentation-element>potential indentation element</dfn> is either a <code class=external data-anolis-spec=html title="the blockquote element"><a href=http://www.whatwg.org/html/#the-blockquote-element>blockquote</a></code>, or a
 <code class=external data-anolis-spec=html title="the div element"><a href=http://www.whatwg.org/html/#the-div-element>div</a></code> that has a <code class=external data-anolis-spec=html title="the style attribute"><a href=http://www.whatwg.org/html/#the-style-attribute>style</a></code> attribute that sets "margin" or some subproperty
 of it.
--- a/implementation.js	Sun May 08 13:54:16 2011 -0600
+++ b/implementation.js	Sun May 08 15:17:33 2011 -0600
@@ -2501,9 +2501,43 @@
 			}
 		}
 
-		// "Outdent each member of node list."
-		for (var i = 0; i < nodeList.length; i++) {
-			outdentNode(nodeList[i]);
+		// "While node list is not empty:"
+		while (nodeList.length) {
+			// "While the first member of node list is an ol or ul or is not
+			// the child of an ol or ul, outdent it and remove it from node
+			// list."
+			while (nodeList.length
+			&& (isHtmlElement(nodeList[0], "OL")
+			|| isHtmlElement(nodeList[0], "UL")
+			|| (!isHtmlElement(nodeList[0].parentNode, "OL")
+			&& !isHtmlElement(nodeList[0].parentNode, "UL")))) {
+				outdentNode(nodeList.shift());
+			}
+
+			// "If node list is empty, break from these substeps."
+			if (!nodeList.length) {
+				break;
+			}
+
+			// "Let sublist be a list of nodes, initially empty."
+			var sublist = [];
+
+			// "Remove the first member of node list and append it to sublist."
+			sublist.push(nodeList.shift());
+
+			// "While the first member of node list is the nextSibling of the
+			// last member of sublist, and the first member of node list is not
+			// an ol or ul, remove the first member of node list and append it
+			// to sublist."
+			while (nodeList.length
+			&& nodeList[0] == sublist[sublist.length - 1].nextSibling
+			&& !isHtmlElement(nodeList[0], "OL")
+			&& !isHtmlElement(nodeList[0], "UL")) {
+				sublist.push(nodeList.shift());
+			}
+
+			// "List-outdent sublist."
+			listOutdent(sublist);
 		}
 		break;
 
@@ -2856,60 +2890,6 @@
 		return;
 	}
 
-	// "If node is an li whose parent is an ol or ul:"
-	if (isHtmlElement(node, "LI")
-	&& (isHtmlElement(node.parentNode, "OL")
-	|| isHtmlElement(node.parentNode, "UL"))) {
-		// "Let parent be the parent of node."
-		var parent_ = node.parentNode;
-
-		// "If parent's parent is not an ol or ul, unset the value attribute of
-		// node if set, then set the tag name of node to "div" and set node to
-		// the result."
-		if (!isHtmlElement(parent_.parentNode, "OL")
-		&& !isHtmlElement(parent_.parentNode, "UL")) {
-			node.removeAttribute("value");
-			node = setTagName(node, "div");
-		}
-
-		// "If node's previousSibling is null, insert node into the parent of
-		// parent immediately before parent, preserving ranges, then abort
-		// these steps."
-		if (!node.previousSibling) {
-			movePreservingRanges(node, parent_.parentNode, getNodeIndex(parent_));
-			return;
-		}
-
-		// "If node's nextSibling is null, insert node into the parent of
-		// parent immediately after parent, preserving ranges, then abort these
-		// steps."
-		if (!node.nextSibling) {
-			movePreservingRanges(node, parent_.parentNode, 1 + getNodeIndex(parent_));
-			return;
-		}
-
-		// "Let new parent be the result of calling cloneNode(false) on
-		// parent."
-		var newParent = parent_.cloneNode(false);
-
-		// "Insert new parent into the parent of parent immediately after
-		// parent."
-		parent_.parentNode.insertBefore(newParent, parent_.nextSibling);
-
-		// "While node's nextSibling is not null, insert the last child of
-		// parent as the first child of new parent, preserving ranges."
-		while (node.nextSibling) {
-			movePreservingRanges(parent_.lastChild, newParent, 0);
-		}
-
-		// "Insert node into the parent of parent immediately after parent,
-		// preserving ranges."
-		movePreservingRanges(node, parent_.parentNode, 1+ getNodeIndex(parent_));
-
-		// "Abort these steps."
-		return;
-	}
-
 	// "If node is an indentation element:"
 	if (isIndentationElement(node)) {
 		// "If node's last child and nextSibling are both inline nodes or its
@@ -3031,6 +3011,139 @@
 	outdentNode(originalAncestor);
 }
 
+function listOutdent(nodeList) {
+	// "Let parent be the parent of the first member of node list."
+	var parent_ = nodeList[0].parentNode;
+
+	// "If the previousSibling of the first member of node list is null:"
+	if (!nodeList[0].previousSibling) {
+		// "For each node in node list:"
+		for (var i = 0; i < nodeList.length; i++) {
+			var node = nodeList[i];
+
+			// "If node is an li, unset its value attribute if set."
+			if (isHtmlElement(node, "LI")) {
+				node.removeAttribute("value");
+			}
+
+			// "If node is an li with no attributes, and the parent of parent
+			// is not an ol or ul:"
+			if (isHtmlElement(node, "LI")
+			&& !node.attributes.length
+			&& !isHtmlElement(parent_.parentNode, "OL")
+			&& !isHtmlElement(parent_.parentNode, "UL")) {
+				// "If the previousSibling of parent and the first child of
+				// node are both inline nodes, let br be the result of calling
+				// createElement("br") on the ownerDocument of node, then
+				// insert br into the parent of parent immediately before
+				// parent."
+				if (isInlineNode(parent_.previousSibling)
+				&& isInlineNode(node.firstChild)) {
+					var br = node.ownerDocument.createElement("br");
+					parent_.parentNode.insertBefore(br, parent_);
+				}
+
+				// "While node has children, insert the first child of node
+				// into the parent of parent immediately before parent,
+				// preserving ranges."
+				while (node.hasChildNodes()) {
+					movePreservingRanges(node.firstChild, parent_.parentNode, getNodeIndex(parent_));
+				}
+
+				// "Remove node from its parent."
+				node.parentNode.removeChild(node);
+
+			// "Otherwise:"
+			} else {
+				// "Insert node into the parent of parent immediately before
+				// parent, preserving ranges."
+				movePreservingRanges(node, parent_.parentNode, getNodeIndex(parent_));
+
+				// "If node is an li and the parent of parent is not an ol or
+				// ul, set the tag name of node to div."
+				if (isHtmlElement(node, "LI")
+				&& !isHtmlElement(parent_.parentNode, "OL")
+				&& !isHtmlElement(parent_.parentNode, "UL")) {
+					setTagName(node, "div");
+				}
+			}
+		}
+
+		// "Abort these steps."
+		return;
+	}
+
+	// "If the nextSibling of the last member of node list is not null:"
+	if (nodeList[nodeList.length - 1].nextSibling) {
+		// "Let new parent be the result of calling cloneNode(false) on
+		// parent."
+		var newParent = parent_.cloneNode(false);
+
+		// "Insert new parent into the parent of parent immediately after
+		// parent."
+		parent_.parentNode.insertBefore(newParent, parent_.nextSibling);
+
+		// "While nextSibling of the last member of node list is not null,
+		// insert the last child of parent as the first child of new
+		// parent, preserving ranges."
+		while (nodeList[nodeList.length - 1].nextSibling) {
+			movePreservingRanges(parent_.lastChild, newParent, 0);
+		}
+	}
+
+	// "For each node in node list, in reverse order:"
+	for (var i = nodeList.length - 1; i >= 0; i--) {
+		var node = nodeList[i];
+
+		// "If node is an li, unset its value attribute if set."
+		if (isHtmlElement(node, "LI")) {
+			node.removeAttribute("value");
+		}
+
+		// "If node is an li with no attributes, and the parent of parent
+		// is not an ol or ul:"
+		if (isHtmlElement(node, "LI")
+		&& !node.attributes.length
+		&& !isHtmlElement(parent_.parentNode, "OL")
+		&& !isHtmlElement(parent_.parentNode, "UL")) {
+			// "If the last child of node and the nextSibling of parent are
+			// both inline nodes, let br be the result of calling
+			// createElement("br") on the ownerDocument of node, then
+			// insert br into the parent of parent immediately after
+			// parent."
+			if (isInlineNode(node.lastChild)
+			&& isInlineNode(parent_.nextSibling)) {
+				var br = node.ownerDocument.createElement("br");
+				parent_.parentNode.insertBefore(br, parent_.nextSibling);
+			}
+
+			// "While node has children, insert the last child of node into
+			// the parent of parent immediately after parent, preserving
+			// ranges."
+			while (node.hasChildNodes()) {
+				movePreservingRanges(node.lastChild, parent_.parentNode, 1 + getNodeIndex(parent_));
+			}
+
+			// "Remove node from its parent."
+			node.parentNode.removeChild(node);
+
+		// "Otherwise:"
+		} else {
+			// "Insert node into the parent of parent immediately after
+			// parent, preserving ranges."
+			movePreservingRanges(node, parent_.parentNode, 1 + getNodeIndex(parent_));
+
+			// "If node is an li and the parent of parent is not an ol or
+			// ul, set the tag name of node to div."
+			if (isHtmlElement(node, "LI")
+			&& !isHtmlElement(parent_.parentNode, "OL")
+			&& !isHtmlElement(parent_.parentNode, "UL")) {
+				setTagName(node, "div");
+			}
+		}
+	}
+}
+
 // "A potential indentation element is either a blockquote, or a div that has a
 // style attribute that sets "margin" or some subproperty of it."
 function isPotentialIndentationElement(node) {
--- a/source.html	Sun May 08 13:54:16 2011 -0600
+++ b/source.html	Sun May 08 15:17:33 2011 -0600
@@ -1999,7 +1999,7 @@
   the result.
 
   <!-- If the first line box in a block box starts with a non-preserved line
-  break, the line break will create an empty line box.  If the line line box in
+  break, the line break will create an empty line box.  If the last line box in
   a block box ends with a non-preserved line break, however, it will not create
   an extra line box.  This means that if you have foo<br>bar, there will be no
   empty line in between, but if you change it to <div>foo</div><br>bar, you've
@@ -2623,7 +2623,27 @@
     <var>node list</var>.
   </ol>
 
-  <li><span>Outdent</span> each member of <var>node list</var>.
+  <li>While <var>node list</var> is not empty:
+
+  <ol>
+    <li>While the first member of <var>node list</var> is an [[ol]] or [[ul]]
+    or is not the [[child]] of an [[ol]] or [[ul]], <span>outdent</span> it and
+    remove it from <var>node list</var>.
+
+    <li>If <var>node list</var> is empty, break from these substeps.
+
+    <li>Let <var>sublist</var> be a list of [[nodes]], initially empty.
+
+    <li>Remove the first member of <var>node list</var> and append it to
+    <var>sublist</var>.
+
+    <li>While the first member of <var>node list</var> is the [[nextsibling]]
+    of the last member of <var>sublist</var>, and the first member of <var>node
+    list</var> is not an [[ol]] or [[ul]], remove the first member of <var>node
+    list</var> and append it to <var>sublist</var>.
+
+    <li><span>List-outdent</span> <var>sublist</var>.
+  </ol>
 </ol>
 
 <p>To <dfn>outdent</dfn> a [[node]] <var>node</var>:
@@ -2650,8 +2670,6 @@
 
   <p class=XXX>Handle this better for nested editable/non-editable.
 
-  <!-- First we handle the list case, because it's simpler. -->
-
   <li>If <var>node</var> is an [[ol]] or [[ul]] with no attributes except
   possibly <code data-anolis-spec=html title=attr-ol-reversed>reversed</code>,
   <code data-anolis-spec=html title=attr-ol-start>start</code>, and/or <code
@@ -2712,50 +2730,6 @@
     <li>Abort these steps.
   </ol>
 
-  <li>If <var>node</var> is an [[li]] whose [[parent]] is an [[ol]] or [[ul]]:
-
-  <ol>
-    <li>Let <var>parent</var> be the [[parent]] of <var>node</var>.
-
-    <li>If <var>parent</var>'s [[parent]] is not an [[ol]] or [[ul]], unset the
-    <code data-anolis-spec=html title=attr-li-value>value</code> attribute of
-    <var>node</var> if set, then <span>set the tag name</span> of
-    <var>node</var> to "div" and set <var>node</var> to the result.
-
-    <li>If <var>node</var>'s [[previoussibling]] is null, insert
-    <var>node</var> into the [[parent]] of <var>parent</var> immediately before
-    <var>parent</var>, <span>preserving ranges</span>, then abort these steps.
-
-    <li>If <var>node</var>'s [[nextsibling]] is null, insert <var>node</var>
-    into the [[parent]] of <var>parent</var> immediately after
-    <var>parent</var>, <span>preserving ranges</span>, then abort these steps.
-
-    <!-- Otherwise, have to split up the list. -->
-
-    <li>Let <var>new parent</var> be the result of calling <code
-    data-anolis-spec=domcore title=dom-Node-cloneNode>cloneNode(false)</code>
-    on <var>parent</var>.
-
-    <p class=XXX>This will duplicate id's, as well as other bad things.  Do we
-    care?  We don't want to not copy attributes at all, because that wouldn't
-    copy style attributes, and Firefox in CSS mode will actually add style
-    attributes to any elements it feels like.  If we do exclude id's, even
-    though they'd only occur if someone manually added them, do we want to
-    exclude other things like itemid or accesskey or . . .
-
-    <li>Insert <var>new parent</var> into the [[parent]] of <var>parent</var>
-    immediately after <var>parent</var>, <span>preserving ranges</span>.
-
-    <li>While <var>node</var>'s [[nextsibling]] is not null, insert the last
-    [[child]] of <var>parent</var> as the first [[child]] of <var>new
-    parent</var>, <span>preserving ranges</span>.
-
-    <li>Insert <var>node</var> into the [[parent]] of <var>parent</var>
-    immediately after <var>parent</var>.
-
-    <li>Abort these steps.
-  </ol>
-
   <!-- The easy case is when the whole element is indented.  In this case we
   remove the whole thing indiscriminately.  In the case of blockquotes
   created by IE, this might change the direction of some children, but then
@@ -2890,6 +2864,119 @@
   <li><span>Outdent</span> <var>original ancestor</var>.
 </ol>
 
+<p>To <dfn>list-outdent</dfn> a list <var>node list</var> of consecutive
+[[sibling]] [[nodes]]:
+
+<ol>
+  <li>Let <var>parent</var> be the [[parent]] of the first member of <var>node
+  list</var>.
+
+  <li>If the [[previoussibling]] of the first member of <var>node list</var> is
+  null:
+
+  <ol>
+    <li>For each <var>node</var> in <var>node list</var>:
+
+    <ol>
+      <li>If <var>node</var> is an [[li]], unset its <code data-anolis-spec=html
+      title=attr-li-value>value</code> attribute if set.
+
+      <li>If <var>node</var> is an [[li]] with no attributes, and the [[parent]]
+      of <var>parent</var> is not an [[ol]] or [[ul]]:
+
+      <ol>
+        <li>If the [[previoussibling]] of <var>parent</var> and the first
+        [[child]] of <var>node</var> are both <span title="inline node">inline
+        nodes</span>, let <var>br</var> be the result of calling <code
+        data-anolis-spec=domcore
+        title=dom-Document-createElement>createElement("br")</code> on the
+        [[ownerdocument]] of <var>node</var>, then insert <var>br</var> into
+        the [[parent]] of <var>parent</var> immediately before
+        <var>parent</var>.
+
+        <li>While <var>node</var> has [[children]], insert the first [[child]] of
+        <var>node</var> into the [[parent]] of <var>parent</var> immediately
+        before <var>parent</var>, <span>preserving ranges</span>.
+
+        <li>Remove <var>node</var> from its [[parent]].
+      </ol>
+
+      <li>Otherwise:
+
+      <ol>
+        <li>Insert <var>node</var> into the [[parent]] of <var>parent</var>
+        immediately before <var>parent</var>, <span>preserving ranges</span>.
+
+        <li>If <var>node</var> is an [[li]] and the [[parent]] of
+        <var>parent</var> is not an [[ol]] or [[ul]], <span>set the tag
+        name</span> of <var>node</var> to [[div]].
+      </ol>
+    </ol>
+
+    <li>Abort these steps.
+  </ol>
+
+  <li>If the [[nextsibling]] of the last member of <var>node list</var> is not
+  null:
+
+  <ol>
+    <li>Let <var>new parent</var> be the result of calling <code
+    data-anolis-spec=domcore title=dom-Node-cloneNode>cloneNode(false)</code>
+    on <var>parent</var>.
+
+    <p class=XXX>This will duplicate id's, as well as other bad things.  Do we
+    care?  We don't want to not copy attributes at all, because that wouldn't
+    copy style attributes, and Firefox in CSS mode will actually add style
+    attributes to any elements it feels like.  If we do exclude id's, even
+    though they'd only occur if someone manually added them, do we want to
+    exclude other things like itemid or accesskey or . . .
+
+    <li>Insert <var>new parent</var> into the [[parent]] of <var>parent</var>
+    immediately after <var>parent</var>.
+
+    <li>While [[nextsibling]] of the last member of <var>node list</var> is not
+    null, insert the last [[child]] of <var>parent</var> as the first [[child]]
+    of <var>new parent</var>, <span>preserving ranges</span>.
+  </ol>
+
+  <li>For each <var>node</var> in <var>node list</var>, <em>in reverse
+  order</em>:
+
+  <ol>
+    <li>If <var>node</var> is an [[li]], unset its <code data-anolis-spec=html
+    title=attr-li-value>value</code> attribute if set.
+
+    <li>If <var>node</var> is an [[li]] with no attributes, and the [[parent]]
+    of <var>parent</var> is not an [[ol]] or [[ul]]:
+
+    <ol>
+      <li>If the last [[child]] of <var>node</var> and the [[nextsibling]] of
+      <var>parent</var> are both <span title="inline node">inline nodes</span>,
+      let <var>br</var> be the result of calling <code data-anolis-spec=domcore
+      title=dom-Document-createElement>createElement("br")</code> on the
+      [[ownerdocument]] of <var>node</var>, then insert <var>br</var> into the
+      [[parent]] of <var>parent</var> immediately after <var>parent</var>.
+
+      <li>While <var>node</var> has [[children]], insert the last [[child]] of
+      <var>node</var> into the [[parent]] of <var>parent</var> immediately
+      after <var>parent</var>, <span>preserving ranges</span>.
+
+      <li>Remove <var>node</var> from its [[parent]].
+    </ol>
+
+    <li>Otherwise:
+
+    <ol>
+      <li>Insert <var>node</var> into the [[parent]] of <var>parent</var>
+      immediately after <var>parent</var>, <span>preserving ranges</span>.
+
+      <li>If <var>node</var> is an [[li]] and the [[parent]] of
+      <var>parent</var> is not an [[ol]] or [[ul]], <span>set the tag
+      name</span> of <var>node</var> to [[div]].
+    </ol>
+  </ol>
+</ol>
+
 <p>A <dfn>potential indentation element</dfn> is either a [[blockquote]], or a
 [[div]] that has a [[style]] attribute that sets "margin" or some subproperty
 of it.