Clean up and refactor outdent
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Mon, 16 May 2011 14:40:09 -0600
changeset 125 7724c3ee154a
parent 124 818c3e2af710
child 126 f090afeb85f9
Clean up and refactor outdent
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Mon May 16 13:23:48 2011 -0600
+++ b/autoimplementation.html	Mon May 16 14:40:09 2011 -0600
@@ -958,6 +958,17 @@
 		'<ol><li>foo<ol><li>[bar]</ol>baz</ol>',
 		'<ol><li>foo<ol><li>bar</ol>[baz]</ol>',
 		'<ol><li>[foo<ol><li>bar]</ol>baz</ol>',
+
+		// Random stuff
+		'foo<ol><li>[bar<li>baz]</ol>quz',
+		'foo<ol start=5><li>[bar]</ol>baz',
+		'foo<ol id=abc><li>[bar]</ol>baz',
+		'foo<ol style=color:red><li>[bar]</ol>baz',
+		'foo<ol><li value=5>[bar]</ol>baz',
+		'foo<ol><li id=abc>[bar]</ol>baz',
+		'foo<ol><li style=color:red>[bar]</ol>baz',
+		'<ol><li>foo</li><ol><li value=5>[bar]</ol></ol>',
+		'<ul><li>foo</li><ol><li value=5>[bar]</ol></ul>',
 	],
 	removeformat: [
 		'foo[]bar',
--- a/editcommands.html	Mon May 16 13:23:48 2011 -0600
+++ b/editcommands.html	Mon May 16 14:40:09 2011 -0600
@@ -2992,12 +2992,33 @@
   <li>For each <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> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">node list</var>:
 
   <ol>
-    <li>If some <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a> of <var title="">node</var> is in <var title="">node list</var>,
+    <li>If the last member of <var title="">node list</var> is an <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a> of
+    <var title="">node</var>, or if <var title="">node</var> is not <a href=#editable>editable</a>,
     continue with the next <var title="">node</var>.
 
-    <li>If <var title="">node</var> has no <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>, 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 <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 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>, append it to
-    <var title="">node list</var>.
+    <!--
+    This step is kind of weird.  For regular outdenting, we start at the inside
+    and outdent going out, so that we remove the innermost indentation, on the
+    theory that that will produce the cleanest markup (remove the most nodes).
+    For lists, we remove the outermost indentation, because it makes a
+    difference whether we remove inner or outer indentation, and logically we
+    want to remove outer.  E.g.,
+
+      <ol><li>foo</li><ul><li>bar</li></ul></ol>
+
+    should become
+
+      foo<ul><li>bar</li></ul>
+
+    not
+
+      foo<ol><li>bar</li></ol>.
+
+    But this is a bit weird and I'm wondering if it's really correct.
+    -->
+    <li>If <var title="">node</var> has no <a href=#editable>editable</a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendants</a>, 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 <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 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>, append it to <var title="">node list</var>.
   </ol>
 
   <li>While <var title="">node list</var> is not empty:
@@ -3045,8 +3066,6 @@
 <ol>
   <li>If <var title="">node</var> is not <a href=#editable>editable</a>, abort these steps.
 
-  <p class=XXX>Handle this better for nested editable/non-editable.
-
   <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
@@ -3057,7 +3076,7 @@
   <div class=XXX>
   <p>We don't handle a case like
 
-  </p><xmp><ol><ol style="color: red"><li>foo<li>bar</ol><li>baz</xmp>
+  </p><xmp><ol><ol style="color: red"><li>foo<li>bar</ol><li>baz</ol></xmp>
 
   <p>If the inner <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> is selected to be outdented, "foo" and "bar" will stop
   being red.  It seems nontrivial to handle this case in general, since we
@@ -3066,20 +3085,11 @@
   </div>
 
   <ol>
-    <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>:
-
-    <ol>
-      <li>Let <var title="">child</var> be 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>.
-
-      <li>If <var title="">child</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="">node</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="">child</var> to "div", and set <var title="">child</var> to the result.
-
-      <li>Insert <var title="">child</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="">node</var>
-      immediately before <var title="">node</var>, <a href=#preserving-ranges>preserving ranges</a>.
-    </ol>
-
-    <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>.
+    <li>Let <var title="">children</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>children</a> of <var title="">node</var>.
+
+    <li>Remove <var title="">node</var>, <a href=#preserving-its-descendants>preserving its descendants</a>.
+
+    <li><a href=#fix-orphaned-list-items>Fix orphaned list items</a> in <var title="">children</var>.
 
     <li>Abort these steps.
   </ol>
@@ -3092,13 +3102,11 @@
     <li>Unset the <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 <code class=external data-anolis-spec=html title=attr-ol-type><a href=http://www.whatwg.org/html/#attr-ol-type>type</a></code> attributes of <var title="">node</var>, if any are
     set.
 
-    <li><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>For each <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> <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> <var title="">child</var> of <var title="">node</var>, 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="">child</var> if set, then <a href=#set-the-tag-name>set the tag name</a> of
-    <var title="">child</var> to "div".
+    <li>Let <var title="">children</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>children</a> of <var title="">node</var>.
+
+    <li><a href=#set-the-tag-name>Set the tag name</a> of <var title="">node</var> to "div".
+
+    <li><a href=#fix-orphaned-list-items>Fix orphaned list items</a> in <var title="">children</var>.
 
     <li>Abort these steps.
   </ol>
@@ -3108,31 +3116,9 @@
   created by IE, this might change the direction of some children, but then
   their direction was probably changed incorrectly in the first place, so no
   harm. -->
-  <li>If <var title="">node</var> is an <a href=#indentation-element>indentation element</a>:
-
-  <ol>
-    <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> and 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> are both
-    <a href=#inline-node title="inline node">inline nodes</a>, and its <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code>
-    is not a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, then call <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>, and insert the result into
-    <var title="">node</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> immediately before <var title="">node</var>.
-
-    <li>If <var title="">node</var>'s last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> and <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> are both <a href=#inline-node title="inline node">inline nodes</a>, and its 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 not a
-    <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, then call <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>, and insert the result into
-    <var title="">node</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> immediately after <var title="">node</var>.
-
-    <!-- Just to be pedantic . . . it could happen! -->
-    <li>If <var title="">node</var> has no <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>, 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> and
-    <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> are both <a href=#inline-node title="inline node">inline nodes</a>, and
-    its <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> is not a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, then call <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>, and insert the result into
-    <var title="">node</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> immediately before <var title="">node</var>.
-
-    <li>Remove <var title="">node</var>, <a href=#preserving-its-descendants>preserving its descendants</a>.
-
-    <li>Abort these steps.
-  </ol>
+  <li>If <var title="">node</var> is an <a href=#indentation-element>indentation element</a>, remove
+  <var title="">node</var>, <a href=#preserving-its-descendants>preserving its descendants</a>.  Then abort these
+  steps.
 
   <!-- No browser handles the case of Firefox 4.0 in CSS mode, where it adds a
   margin attribute to an existing element, including Firefox itself.  So let's
@@ -3143,9 +3129,6 @@
   Firefox in CSS mode, for instance (color, font-family, etc.). -->
 
   <ol>
-    <li><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>Unset the <code class=external data-anolis-spec=html title=classes><a href=http://www.whatwg.org/html/#classes>class</a></code> and
     <code class=external data-anolis-spec=html title="the dir attribute"><a href=http://www.whatwg.org/html/#the-dir-attribute>dir</a></code>
     attributes of <var title="">node</var>, if any.
@@ -3153,6 +3136,8 @@
     <li>Unset the margin, padding, and border CSS properties of
     <var title="">node</var>.
 
+    <li><a href=#set-the-tag-name>Set the tag name</a> of <var title="">node</var> to "div".
+
     <li>Abort these steps.
   </ol>
 
@@ -3256,16 +3241,28 @@
   <li><a href=#split-the-parent>Split the parent</a> of <var title="">node list</var>, with <var title="">new
   parent</var> null.
 
-  <li>For each <var title="">node</var> in <var title="">node list</var>:
+  <li><a href=#fix-orphaned-list-items>Fix orphaned list items</a> in <var title="">node list</var>.
+</ol>
+
+<p>To <dfn id=fix-orphaned-list-items>fix orphaned list items</dfn> in 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> <var title="">node
+list</var>:
+
+<ol>
+  <li>For each <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> <var title="">item</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> with no attributes and 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>
-    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 <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>,
-    <a href=#preserving-its-descendants>preserving its descendants</a>.
-
-    <li>Otherwise, 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 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> 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
-    "div".
+      <li>If <var title="">item</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>, unset
+      <var title="">item</var>'s <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.
+      <!-- IE9, Firefox 4.0, and Opera 11.10 keep the value attribute even if
+      the parent is now a ul.  Chrome 12 dev strips it even if the parent is
+      now an ol.  The spec makes more sense. -->
+
+      <li>If <var title="">item</var> has no attributes and 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> 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 <var title="">item</var>, <a href=#preserving-its-descendants>preserving its
+      descendants</a>.
+
+      <li>Otherwise, if <var title="">item</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>, <a href=#set-the-tag-name>set the tag name</a> of <var title="">item</var> to "div".
   </ol>
 </ol>
 
--- a/implementation.js	Mon May 16 13:23:48 2011 -0600
+++ b/implementation.js	Mon May 16 14:40:09 2011 -0600
@@ -584,6 +584,16 @@
 		&& (isEditingHost(node.parentNode) || isEditable(node.parentNode));
 }
 
+function hasEditableDescendants(node) {
+	for (var i = 0; i < node.childNodes.length; i++) {
+		if (isEditable(node.childNodes[i])
+		|| hasEditableDescendants(node.childNodes[i])) {
+			return true;
+		}
+	}
+	return false;
+}
+
 /**
  * "A Node is effectively contained in a Range if either it is contained in the
  * Range; or it is the Range's start node, it is a Text node, and its length is
@@ -2851,18 +2861,16 @@
 				continue;
 			}
 
-			// "If some ancestor of node is in node list, continue with the
-			// next node."
-			//
-			// We only need to check the last node on the list, if you think
-			// about it.
-			if (isAncestor(nodeList[nodeList.length - 1], node)) {
+			// "If the last member of node list is an ancestor of node, or if
+			// node is not editable, continue with the next node."
+			if (isAncestor(nodeList[nodeList.length - 1], node)
+			|| !isEditable(node)) {
 				continue;
 			}
 
-			// "If node has no children, or is an ol or ul, or is an li whose
-			// parent is an ol or ul, append it to node list."
-			if (!node.hasChildNodes()
+			// "If node has no editable descendants, or is an ol or ul, or is
+			// an li whose parent is an ol or ul, append it to node list."
+			if (!hasEditableDescendants(node)
 			|| isHtmlElement(node, "OL")
 			|| isHtmlElement(node, "UL")
 			|| (isHtmlElement(node, "LI")
@@ -3240,27 +3248,14 @@
 		|| isHtmlElement(node.parentNode, "UL")
 		|| [].every.call(node.attributes, function (attr) { return ["reversed", "start", "type"].indexOf(attr.name) != -1 })
 	)) {
-		// "While node has children:"
-		while (node.hasChildNodes()) {
-			// "Let child be the first child of node."
-			var child = node.firstChild;
-
-			// "If child is an li and the parent of node is not an ol or ul,
-			// set the tag name of child to "div", and set child to the
-			// result."
-			if (isHtmlElement(child, "LI")
-			&& !isHtmlElement(node.parentNode, "OL")
-			&& !isHtmlElement(node.parentNode, "UL")) {
-				child = setTagName(child, "div");
-			}
-
-			// "Insert child into the parent of node immediately before node,
-			// preserving ranges."
-			movePreservingRanges(child, node.parentNode, getNodeIndex(node));
-		}
-
-		// "Remove node from its parent."
-		node.parentNode.removeChild(node);
+		// "Let children be the children of node."
+		var children = [].slice.call(node.childNodes);
+
+		// "Remove node, preserving its descendants."
+		removePreservingDescendants(node);
+
+		// "Fix orphaned list items in children."
+		fixOrphanedListItems(children);
 
 		// "Abort these steps."
 		return;
@@ -3275,69 +3270,28 @@
 		node.removeAttribute("start");
 		node.removeAttribute("type");
 
-		// "Set the tag name of node to "div", and set node to the result."
-		node = setTagName(node, "div");
-
-		// "For each li child child of node, unset the value attribute of child
-		// if set, then set the tag name of child to "div"."
-		for (var i = 0; i < node.childNodes.length; i++) {
-			var child = node.childNodes[i];
-
-			if (isHtmlElement(child, "LI")) {
-				child.removeAttribute("value");
-				setTagName(child, "div");
-			}
-		}
+		// "Let children be the children of node."
+		var children = [].slice.call(node.childNodes);
+
+		// "Set the tag name of node to "div"."
+		setTagName(node, "div");
+
+		// "Fix orphaned list items in children."
+		fixOrphanedListItems(children);
 
 		// "Abort these steps."
 		return;
 	}
 
-	// "If node is an indentation element:"
+	// "If node is an indentation element, remove node, preserving its
+	// descendants.  Then abort these steps."
 	if (isIndentationElement(node)) {
-		// "If node's previousSibling and first child are both inline nodes,
-		// and its previousSibling is not a br, then call createElement("br")
-		// on the ownerDocument of node, and insert the result into node's
-		// parent immediately before node."
-		if (isInlineNode(node.previousSibling)
-		&& isInlineNode(node.firstChild)
-		&& !isHtmlElement(node.previousSibling, "BR")) {
-			node.parentNode.insertBefore(node.ownerDocument.createElement("br"), node);
-		}
-
-		// "If node's last child and nextSibling are both inline nodes, and its
-		// last child is not a br, then call createElement("br") on the
-		// ownerDocument of node, and insert the result into node's parent
-		// immediately after node."
-		if (isInlineNode(node.lastChild)
-		&& isInlineNode(node.nextSibling)
-		&& !isHtmlElement(node.lastChild, "BR")) {
-			node.parentNode.insertBefore(node.ownerDocument.createElement("br"), node.nextSibling);
-		}
-
-		// "If node has no children, and its previousSibling and nextSibling
-		// are both inline nodes, and its previousSibling is not a br, then
-		// call createElement("br") on the ownerDocument of node, and insert
-		// the result into node's parent immediately before node."
-		if (!node.hasChildNodes()
-		&& isInlineNode(node.previousSibling)
-		&& isInlineNode(node.nextSibling)
-		&& !isHtmlElement(node.previousSibling, "BR")) {
-			node.parentNode.insertBefore(node.ownerDocument.createElement("br"), node);
-		}
-
-		// "Remove node, preserving its descendants."
 		removePreservingDescendants(node);
-
-		// "Abort these steps."
 		return;
 	}
 
 	// "If node is a potential indentation element:"
 	if (isPotentialIndentationElement(node)) {
-		// "Set the tag name of node to "div", and set node to the result."
-		node = setTagName(node, "div");
-
 		// "Unset the class and dir attributes of node, if any."
 		node.removeAttribute("class");
 		node.removeAttribute("dir");
@@ -3350,6 +3304,9 @@
 			node.removeAttribute("style");
 		}
 
+		// "Set the tag name of node to "div"."
+		setTagName(node, "div");
+
 		// "Abort these steps."
 		return;
 	}
@@ -3444,24 +3401,36 @@
 	// "Split the parent of node list, with new parent null."
 	splitParent(nodeList, null);
 
-	// "For each node in node list:"
+	// "Fix orphaned list items in node list."
+	fixOrphanedListItems(nodeList);
+}
+
+function fixOrphanedListItems(nodeList) {
+	// "For each li item in node list:"
 	for (var i = 0; i < nodeList.length; i++) {
-		var node = nodeList[i];
-
-		// "If node is an li with no attributes and its parent is not an ol or
-		// ul, remove node from its parent, preserving its descendants."
-		if (isHtmlElement(node, "LI")
-		&& !node.attributes.length
-		&& !isHtmlElement(node.parentNode, "OL")
-		&& !isHtmlElement(node.parentNode, "UL")) {
-			removePreservingDescendants(node);
-
-		// "Otherwise, if node is an li and its parent is not an ol or ul, set
-		// the tag name of node to "div"."
-		} else if (isHtmlElement(node, "LI")
-		&& !isHtmlElement(node.parentNode, "OL")
-		&& !isHtmlElement(node.parentNode, "UL")) {
-			setTagName(node, "div");
+		var item = nodeList[i];
+		if (!isHtmlElement(item, "LI")) {
+			continue;
+		}
+
+		// "If item's parent is not an ol, unset item's value attribute, if
+		// set."
+		if (!isHtmlElement(item.parentNode, "OL")) {
+			item.removeAttribute("value");
+		}
+
+		// "If item has no attributes and its parent is not an ol or ul, remove
+		// item, preserving its descendants."
+		if (!item.attributes.length
+		&& !isHtmlElement(item.parentNode, "OL")
+		&& !isHtmlElement(item.parentNode, "UL")) {
+			removePreservingDescendants(item);
+
+		// "Otherwise, if item's parent is not an ol or ul, set the tag name of
+		// item to "div"."
+		} else if (!isHtmlElement(item.parentNode, "OL")
+		&& !isHtmlElement(item.parentNode, "UL")) {
+			setTagName(item, "div");
 		}
 	}
 }
--- a/source.html	Mon May 16 13:23:48 2011 -0600
+++ b/source.html	Mon May 16 14:40:09 2011 -0600
@@ -3040,12 +3040,33 @@
   <li>For each [[node]] <var>node</var> [[contained]] in <var>node list</var>:
 
   <ol>
-    <li>If some [[ancestor]] of <var>node</var> is in <var>node list</var>,
+    <li>If the last member of <var>node list</var> is an [[ancestor]] of
+    <var>node</var>, or if <var>node</var> is not <span>editable</span>,
     continue with the next <var>node</var>.
 
-    <li>If <var>node</var> has no [[children]], or is an [[ol]] or [[ul]], or
-    is an [[li]] whose [[parent]] is an [[ol]] or [[ul]], append it to
-    <var>node list</var>.
+    <!--
+    This step is kind of weird.  For regular outdenting, we start at the inside
+    and outdent going out, so that we remove the innermost indentation, on the
+    theory that that will produce the cleanest markup (remove the most nodes).
+    For lists, we remove the outermost indentation, because it makes a
+    difference whether we remove inner or outer indentation, and logically we
+    want to remove outer.  E.g.,
+
+      <ol><li>foo</li><ul><li>bar</li></ul></ol>
+
+    should become
+
+      foo<ul><li>bar</li></ul>
+
+    not
+
+      foo<ol><li>bar</li></ol>.
+
+    But this is a bit weird and I'm wondering if it's really correct.
+    -->
+    <li>If <var>node</var> has no <span>editable</span> [[descendants]], or is
+    an [[ol]] or [[ul]], or is an [[li]] whose [[parent]] is an [[ol]] or
+    [[ul]], append it to <var>node list</var>.
   </ol>
 
   <li>While <var>node list</var> is not empty:
@@ -3093,8 +3114,6 @@
 <ol>
   <li>If <var>node</var> is not <span>editable</span>, abort these steps.
 
-  <p class=XXX>Handle this better for nested editable/non-editable.
-
   <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
@@ -3106,7 +3125,7 @@
   <div class=XXX>
   <p>We don't handle a case like
 
-  <xmp><ol><ol style="color: red"><li>foo<li>bar</ol><li>baz</xmp>
+  <xmp><ol><ol style="color: red"><li>foo<li>bar</ol><li>baz</ol></xmp>
 
   <p>If the inner [[ol]] is selected to be outdented, "foo" and "bar" will stop
   being red.  It seems nontrivial to handle this case in general, since we
@@ -3115,20 +3134,11 @@
   </div>
 
   <ol>
-    <li>While <var>node</var> has [[children]]:
-
-    <ol>
-      <li>Let <var>child</var> be the first [[child]] of <var>node</var>.
-
-      <li>If <var>child</var> is an [[li]] and the [[parent]] of
-      <var>node</var> is not an [[ol]] or [[ul]], <span>set the tag name</span>
-      of <var>child</var> to "div", and set <var>child</var> to the result.
-
-      <li>Insert <var>child</var> into the [[parent]] of <var>node</var>
-      immediately before <var>node</var>, <span>preserving ranges</span>.
-    </ol>
-
-    <li>Remove <var>node</var> from its [[parent]].
+    <li>Let <var>children</var> be the [[children]] of <var>node</var>.
+
+    <li>Remove <var>node</var>, <span>preserving its descendants</span>.
+
+    <li><span>Fix orphaned list items</span> in <var>children</var>.
 
     <li>Abort these steps.
   </ol>
@@ -3144,13 +3154,11 @@
     title=attr-ol-type>type</code> attributes of <var>node</var>, if any are
     set.
 
-    <li><span>Set the tag name</span> of <var>node</var> to "div", and set
-    <var>node</var> to the result.
-
-    <li>For each [[li]] [[child]] <var>child</var> of <var>node</var>, unset
-    the <code data-anolis-spec=html title=attr-li-value>value</code> attribute
-    of <var>child</var> if set, then <span>set the tag name</span> of
-    <var>child</var> to "div".
+    <li>Let <var>children</var> be the [[children]] of <var>node</var>.
+
+    <li><span>Set the tag name</span> of <var>node</var> to "div".
+
+    <li><span>Fix orphaned list items</span> in <var>children</var>.
 
     <li>Abort these steps.
   </ol>
@@ -3160,36 +3168,9 @@
   created by IE, this might change the direction of some children, but then
   their direction was probably changed incorrectly in the first place, so no
   harm. -->
-  <li>If <var>node</var> is an <span>indentation element</span>:
-
-  <ol>
-    <li>If <var>node</var>'s [[previoussibling]] and first [[child]] are both
-    <span title="inline node">inline nodes</span>, and its [[previoussibling]]
-    is not a [[br]], then call <code data-anolis-spec=domcore
-    title=dom-Document-createElement>createElement("br")</code> on the
-    [[ownerdocument]] of <var>node</var>, and insert the result into
-    <var>node</var>'s [[parent]] immediately before <var>node</var>.
-
-    <li>If <var>node</var>'s last [[child]] and [[nextsibling]] are both <span
-    title="inline node">inline nodes</span>, and its last [[child]] is not a
-    [[br]], then call <code data-anolis-spec=domcore
-    title=dom-Document-createElement>createElement("br")</code> on the
-    [[ownerdocument]] of <var>node</var>, and insert the result into
-    <var>node</var>'s [[parent]] immediately after <var>node</var>.
-
-    <!-- Just to be pedantic . . . it could happen! -->
-    <li>If <var>node</var> has no [[children]], and its [[previoussibling]] and
-    [[nextsibling]] are both <span title="inline node">inline nodes</span>, and
-    its [[previoussibling]] is not a [[br]], then call <code
-    data-anolis-spec=domcore
-    title=dom-Document-createElement>createElement("br")</code> on the
-    [[ownerdocument]] of <var>node</var>, and insert the result into
-    <var>node</var>'s [[parent]] immediately before <var>node</var>.
-
-    <li>Remove <var>node</var>, <span>preserving its descendants</span>.
-
-    <li>Abort these steps.
-  </ol>
+  <li>If <var>node</var> is an <span>indentation element</span>, remove
+  <var>node</var>, <span>preserving its descendants</span>.  Then abort these
+  steps.
 
   <!-- No browser handles the case of Firefox 4.0 in CSS mode, where it adds a
   margin attribute to an existing element, including Firefox itself.  So let's
@@ -3200,9 +3181,6 @@
   Firefox in CSS mode, for instance (color, font-family, etc.). -->
 
   <ol>
-    <li><span>Set the tag name</span> of <var>node</var> to "div", and set
-    <var>node</var> to the result.
-
     <li>Unset the <code data-anolis-spec=html title=classes>class</code> and
     <code data-anolis-spec=html title="the dir attribute">dir</code>
     attributes of <var>node</var>, if any.
@@ -3210,6 +3188,8 @@
     <li>Unset the margin, padding, and border CSS properties of
     <var>node</var>.
 
+    <li><span>Set the tag name</span> of <var>node</var> to "div".
+
     <li>Abort these steps.
   </ol>
 
@@ -3313,16 +3293,29 @@
   <li><span>Split the parent</span> of <var>node list</var>, with <var>new
   parent</var> null.
 
-  <li>For each <var>node</var> in <var>node list</var>:
+  <li><span>Fix orphaned list items</span> in <var>node list</var>.
+</ol>
+
+<p>To <dfn>fix orphaned list items</dfn> in a list of [[nodes]] <var>node
+list</var>:
+
+<ol>
+  <li>For each [[li]] <var>item</var> in <var>node list</var>:
 
   <ol>
-    <li>If <var>node</var> is an [[li]] with no attributes and its [[parent]]
-    is not an [[ol]] or [[ul]], remove <var>node</var> from its [[parent]],
-    <span>preserving its descendants</span>.
-
-    <li>Otherwise, if <var>node</var> is an [[li]] and its [[parent]] is not an
-    [[ol]] or [[ul]], <span>set the tag name</span> of <var>node</var> to
-    "div".
+      <li>If <var>item</var>'s [[parent]] is not an [[ol]], unset
+      <var>item</var>'s <code data-anolis-spec=html
+      title=attr-li-value>value</code> attribute, if set.
+      <!-- IE9, Firefox 4.0, and Opera 11.10 keep the value attribute even if
+      the parent is now a ul.  Chrome 12 dev strips it even if the parent is
+      now an ol.  The spec makes more sense. -->
+
+      <li>If <var>item</var> has no attributes and its [[parent]] is not an
+      [[ol]] or [[ul]], remove <var>item</var>, <span>preserving its
+      descendants</span>.
+
+      <li>Otherwise, if <var>item</var>'s [[parent]] is not an [[ol]] or
+      [[ul]], <span>set the tag name</span> of <var>item</var> to "div".
   </ol>
 </ol>