Avoid double-indentation of list items
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Thu, 05 May 2011 15:33:48 -0600
changeset 92 36c20a06b86e
parent 91 930e3d99e27a
child 93 2453158b39fc
Avoid double-indentation of list items

Also in a non-list context, use nextSiblings for merging if available,
and don't double-indent there either.
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Thu May 05 15:20:38 2011 -0600
+++ b/autoimplementation.html	Thu May 05 15:33:48 2011 -0600
@@ -499,33 +499,45 @@
 		'<blockquote><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
 		'<blockquote><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
 		'<blockquote><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+		'<p>[foo]<blockquote><p>bar</blockquote><p>extra',
+		'<p>[foo<blockquote><p>b]ar</blockquote><p>extra',
 
 		// IE:
 		'<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
 		'<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
 		'<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
 		'<blockquote style="margin-right: 0" dir="ltr"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+		'<p>[foo]<blockquote style="margin-right: 0" dir="ltr"><p>bar</blockquote><p>extra',
+		'<p>[foo<blockquote style="margin-right: 0" dir="ltr"><p>b]ar</blockquote><p>extra',
 
 		// Firefox CSS mode:
 		'<p style="margin-left: 40px">foo[bar]</p><p style="margin-left: 40px">baz</p><p>extra',
 		'<p style="margin-left: 40px">foo[bar</p><p style="margin-left: 40px">b]az</p><p>extra',
 		'<p style="margin-left: 40px">foo[bar]</p><p>baz</p><p>extra',
 		'<p style="margin-left: 40px">foo[bar</p><p>b]az</p><p>extra',
+		'<p>[foo]<p style="margin-left: 40px">bar<p>extra',
+		'<p>[foo<p style="margin-left: 40px">b]ar<p>extra',
 
 		// WebKit:
 		'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
 		'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
 		'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
 		'<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+		'<p>[foo]<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>bar</blockquote><p>extra',
+		'<p>[foo<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px"><p>b]ar</blockquote><p>extra',
 
 		// Spec:
 		'<blockquote style="margin: 0 40px"><p>foo[bar]</p><p>baz</p></blockquote><p>extra',
 		'<blockquote style="margin: 0 40px"><p>foo[bar</p><p>b]az</p></blockquote><p>extra',
 		'<blockquote style="margin: 0 40px"><p>foo[bar]</p></blockquote><p>baz</p><p>extra',
 		'<blockquote style="margin: 0 40px"><p>foo[bar</p></blockquote><p>b]az</p><p>extra',
+		'<p>[foo]<blockquote style="margin: 0 40px"><p>bar</blockquote><p>extra',
+		'<p>[foo<blockquote style="margin: 0 40px"><p>b]ar</blockquote><p>extra',
 		'<div style="margin: 0 40px"><p>foo[bar]</p><p>baz</p></div><p>extra',
 		'<div style="margin: 0 40px"><p>foo[bar</p><p>b]az</p></div><p>extra',
 		'<div style="margin: 0 40px"><p>foo[bar]</p></div><p>baz</p><p>extra',
+		'<p>[foo]<div style="margin: 0 40px"><p>bar</div><p>extra',
+		'<p>[foo<div style="margin: 0 40px"><p>b]ar</div><p>extra',
 		'<div style="margin: 0 40px"><p>foo[bar</p></div><p>b]az</p><p>extra',
 
 		// MDC says "In Firefox, if the selection spans multiple lines at
--- a/editcommands.html	Thu May 05 15:20:38 2011 -0600
+++ b/editcommands.html	Thu May 05 15:33:48 2011 -0600
@@ -1986,33 +1986,73 @@
   <!-- Otherwise the last child of the previous sibling might be a list, which
   the li wouldn't get appended to. -->
 
-  <li><a href=#indent>Indent</a> each member of <var title="">node list</var>.
+  <li>While <var title="">node list</var> is not empty:
+
+  <ol>
+    <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>, remove the first member of
+    <var title="">node list</var> and append it to <var title="">sublist</var>.
+
+    <li><a href=#indent>Indent</a> <var title="">sublist</var>.
+  </ol>
 </ol>
 
-<p>To <dfn id=indent>indent</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>:
+<!--
+We have to handle entire lists of siblings at once, or else we'd wind up doing
+something like
+
+  <ol>
+    {<li>foo</li>
+    <ol><li>bar</li></ol>}
+  </ol>
+  ->
+  <ol><ol>
+    <li>foo</li>
+    <li>bar</li>
+  </ol></ol>
+  ->
+  <ol><ol><ol>
+    <li>foo</li>
+    <li>bar</li>
+  </ol></ol></ol>
+
+since by the time we got to doing the <ol> that originally contained "bar", we
+won't remember that we aren't supposed to indent "foo" a second time.
+-->
+<p>To <dfn id=indent>indent</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>If <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> 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>:
+  <li>Let <var title="">first node</var> be the first member of <var title="">node list</var>.
+
+  <li>If <var title="">first 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> 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="">tag</var> be the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> of 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 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="">node</var> is an <a href=#html-element>HTML
-    element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">tag</var>, append <var title="">node</var> as
-    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 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>, <a href=#preserving-ranges>preserving
-    ranges</a>.  Then abort these steps.
-
-    <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 <var title="">node</var> is an <a href=#html-element>HTML
-    element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">tag</var>, insert <var title="">node</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 its <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>, <a href=#preserving-ranges>preserving ranges</a>.
+    <var title="">first node</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 an <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">tag</var>, then for
+    each <var title="">node</var> in <var title="">node list</var>, append <var title="">node</var> as 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 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>, <a href=#preserving-ranges>preserving ranges</a>.
     Then abort these steps.
 
+    <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 an
+    <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">tag</var>, then for each
+    <var title="">node</var> in <var title="">node list</var> <em>in reverse order</em>, insert
+    <var title="">node</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 its <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>,
+    <a href=#preserving-ranges>preserving ranges</a>.  Then abort these steps.
+
     <li>Let <var title="">new parent</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(<var title="">tag</var>)</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>.
-
-    <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="">node</var>
-    immediately before <var title="">node</var>.
+    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="">first node</var>.
+
+    <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="">first
+    node</var> immediately before <var title="">first node</var>.
     <!--
     This matches IE9, Firefox 4.0, and Chrome 12 dev.  If there's a preceding
     <li>, Opera 11.10 instead adds the new parent to the end of that <li>, so
@@ -2034,18 +2074,28 @@
     http://www.w3.org/Bugs/Public/show_bug.cgi?id=12609
     -->
 
-    <li>Append <var title="">node</var> as 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="">new parent</var>,
+    <li>For each <var title="">node</var> in <var title="">node list</var>, append
+    <var title="">node</var> as 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="">new parent</var>,
     <a href=#preserving-ranges>preserving ranges</a>.
 
     <li>Abort these steps.
   </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="">node</var> is an <a href=#indentation-element>indentation
-  element</a>; its "display" property computes to "block"; its
-  "margin-left" and "margin-right" properties compute to "40px"; and its
-  "margin-top" and "margin-bottom" properties compute to "0"; then append
-  <var title="">node</var> as 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 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>,
-  <a href=#preserving-ranges>preserving ranges</a>, then abort these steps.
+  <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
+  an <a href=#indentation-element>indentation element</a>; its "display" property computes to
+  "block"; its "margin-left" and "margin-right" properties compute to "40px";
+  and its "margin-top" and "margin-bottom" properties compute to "0"; then
+  for each <var title="">node</var> in <var title="">node list</var>, append <var title="">node</var> as
+  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 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>, <a href=#preserving-ranges>preserving
+  ranges</a>.  Then abort these steps.
+
+  <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 an
+  <a href=#indentation-element>indentation element</a>; its "display" property computes to "block";
+  its "margin-left" and "margin-right" properties compute to "40px"; and its
+  "margin-top" and "margin-bottom" properties compute to "0"; then for each
+  <var title="">node</var> in <var title="">node list</var>, <em>in reverse order</em>, insert
+  <var title="">node</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 its <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>,
+  <a href=#preserving-ranges>preserving ranges</a>.  Then abort these steps.
 
   <li>Let <var title="">tag</var> be "div" if the <a href=#css-styling-flag>CSS styling flag</a> is
   true, otherwise "blockquote".
@@ -2058,10 +2108,10 @@
   non-CSS modes. -->
 
   <li>Let <var title="">new parent</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(<var title="">tag</var>)</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>.
+  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="">first node</var>.
 
   <li>Insert <var title="">new parent</var> 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>.
+  immediately before <var title="">first node</var>.
 
   <li>Set the CSS property "margin" of <var title="">new parent</var> to "0 40px".
 
@@ -2082,8 +2132,9 @@
   margins on both sides.  I can't think of any better way to do this at the
   moment.
 
-  <li>Append <var title="">node</var> as 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="">new parent</var>,
-  <a href=#preserving-ranges>preserving ranges</a>.
+  <li>For each <var title="">node</var> in <var title="">node list</var>, append <var title="">node</var>
+  as 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="">new parent</var>, <a href=#preserving-ranges>preserving
+  ranges</a>.
 </ol>
 
 <dd><strong>State</strong>:
--- a/implementation.js	Thu May 05 15:20:38 2011 -0600
+++ b/implementation.js	Thu May 05 15:33:48 2011 -0600
@@ -2264,9 +2264,24 @@
 			normalizeSublists(nodeList[0].previousSibling);
 		}
 
-		// "Indent each member of node list."
-		for (var i = 0; i < nodeList.length; i++) {
-			indentNode(nodeList[i]);
+		// "While node list is not empty:"
+		while (nodeList.length) {
+			// "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, remove the first member of node list and
+			// append it to sublist."
+			while (nodeList.length
+			&& nodeList[0] == sublist[sublist.length - 1].nextSibling) {
+				sublist.push(nodeList.shift());
+			}
+
+			// "Indent sublist."
+			indentNodes(sublist);
 		}
 		break;
 
@@ -2612,58 +2627,92 @@
 	}
 }
 
-function indentNode(node) {
-	// "If node's parent is an ol or ul:"
-	if (isHtmlElement(node.parentNode, "OL")
-	|| isHtmlElement(node.parentNode, "UL")) {
-		// "Let tag be the local name of the parent of node."
-		var tag = node.parentNode.tagName;
-
-		// "If the previousSibling of node is an HTML element with local name
-		// tag, append node as the last child of its previousSibling,
-		// preserving ranges. Then abort these steps."
-		if (isHtmlElement(node.previousSibling)
-		&& node.previousSibling.tagName == tag) {
-			movePreservingRanges(node, node.previousSibling, getNodeLength(node.previousSibling));
+function indentNodes(nodeList) {
+	// "Let first node be the first member of node list."
+	var firstNode = nodeList[0];
+
+	// "If first node's parent is an ol or ul:"
+	if (isHtmlElement(firstNode.parentNode, "OL")
+	|| isHtmlElement(firstNode.parentNode, "UL")) {
+		// "Let tag be the local name of the parent of first node."
+		var tag = firstNode.parentNode.tagName;
+
+		// "If the previousSibling of the first member of node list is an HTML
+		// element with local name tag, then for each node in node list, append
+		// node as the last child of its previousSibling, preserving ranges.
+		// Then abort these steps."
+		if (isHtmlElement(nodeList[0].previousSibling, tag)) {
+			for (var i = 0; i < nodeList.length; i++) {
+				movePreservingRanges(nodeList[i], nodeList[i].previousSibling, nodeList[i].previousSibling.childNodes.length);
+			}
 			return;
 		}
 
-		// "If the nextSibling of node is an HTML element with local name tag,
-		// insert node as the first child of its nextSibling, preserving
-		// ranges. Then abort these steps."
-		if (isHtmlElement(node.nextSibling)
-		&& node.nextSibling.tagName == tag) {
-			movePreservingRanges(node, node.nextSibling, 0);
+		// "If the nextSibling of the last member of node list is an HTML
+		// element with local name tag, then for each node in node list in
+		// reverse order, insert node as the first child of its nextSibling,
+		// preserving ranges. Then abort these steps."
+		if (isHtmlElement(nodeList[nodeList.length - 1].nextSibling, tag)) {
+			for (var i = nodeList.length - 1; i >= 0; i--) {
+				movePreservingRanges(nodeList[i], nodeList[i].nextSibling, 0);
+			}
 			return;
 		}
 
 		// "Let new parent be the result of calling createElement(tag) on the
-		// ownerDocument of node."
-		var newParent = node.ownerDocument.createElement(tag);
-
-		// "Insert new parent into the parent of node immediately before node."
-		node.parentNode.insertBefore(newParent, node);
-
-		// "Append node as the last child of new parent, preserving ranges."
-		movePreservingRanges(node, newParent, 0);
+		// ownerDocument of first node."
+		var newParent = firstNode.ownerDocument.createElement(tag);
+
+		// "Insert new parent into the parent of first node immediately before
+		// first node."
+		firstNode.parentNode.insertBefore(newParent, firstNode);
+
+		// "For each node in node list, append node as the last child of new
+		// parent, preserving ranges."
+		for (var i = 0; i < nodeList.length; i++) {
+			movePreservingRanges(nodeList[i], newParent, newParent.childNodes.length);
+		}
 
 		// "Abort these steps."
 		return;
 	}
 
-	// "If the previousSibling of node is an indentation element; its "display"
-	// property computes to "block"; its "margin-left" and "margin-right"
-	// properties compute to "40px"; and its "margin-top" and "margin-bottom"
-	// properties compute to "0"; then append node as the last child of its
-	// previousSibling, preserving ranges, then abort these steps."
-	if (isIndentationElement(node.previousSibling)) {
-		var style = getComputedStyle(node.previousSibling);
+	// "If the previousSibling of the first member of node list is an
+	// indentation element; its "display" property computes to "block"; its
+	// "margin-left" and "margin-right" properties compute to "40px"; and its
+	// "margin-top" and "margin-bottom" properties compute to "0"; then for
+	// each node in node list, append node as the last child of its
+	// previousSibling, preserving ranges. Then abort these steps."
+	if (isIndentationElement(nodeList[0].previousSibling)) {
+		var style = getComputedStyle(nodeList[0].previousSibling);
 		if (style.display == "block"
 		&& style.marginLeft == "40px"
 		&& style.marginRight == "40px"
 		&& style.marginTop == "0px"
 		&& style.marginBottom == "0px") {
-			movePreservingRanges(node, node.previousSibling, node.previousSibling.childNodes.length);
+			for (var i = 0; i < nodeList.length; i++) {
+				movePreservingRanges(nodeList[i], nodeList[i].previousSibling, nodeList[i].previousSibling.childNodes.length);
+			}
+			return;
+		}
+	}
+
+	// "If the nextSibling of the last member of node list is an indentation
+	// element; its "display" property computes to "block"; its "margin-left"
+	// and "margin-right" properties compute to "40px"; and its "margin-top"
+	// and "margin-bottom" properties compute to "0"; then for each node in
+	// node list, in reverse order, insert node as the first child of its
+	// nextSibling, preserving ranges. Then abort these steps."
+	if (isIndentationElement(nodeList[nodeList.length - 1].nextSibling)) {
+		var style = getComputedStyle(nodeList[nodeList.length - 1].nextSibling);
+		if (style.display == "block"
+		&& style.marginLeft == "40px"
+		&& style.marginRight == "40px"
+		&& style.marginTop == "0px"
+		&& style.marginBottom == "0px") {
+			for (var i = nodeList.length - 1; i >= 0; i--) {
+				movePreservingRanges(nodeList[i], nodeList[i].nextSibling, 0);
+			}
 			return;
 		}
 	}
@@ -2673,18 +2722,20 @@
 	var tag = cssStylingFlag ? "div" : "blockquote";
 
 	// "Let new parent be the result of calling createElement(tag) on
-	// the ownerDocument of node."
-	var newParent = node.ownerDocument.createElement(tag);
-
-	// "Insert new parent into node's parent immediately before node."
-	node.parentNode.insertBefore(newParent, node);
+	// the ownerDocument of first node."
+	var newParent = firstNode.ownerDocument.createElement(tag);
+
+	// "Insert new parent into node's parent immediately before first node."
+	firstNode.parentNode.insertBefore(newParent, firstNode);
 
 	// "Set the CSS property "margin" of new parent to "0 40px"."
 	newParent.setAttribute("style", "margin: 0 40px");
 
-	// "Append node as the last child of new parent, preserving
-	// ranges."
-	movePreservingRanges(node, newParent, 0);
+	// "For each node in node list, append node as the last child of new
+	// parent, preserving ranges."
+	for (var i = 0; i < nodeList.length; i++) {
+		movePreservingRanges(nodeList[i], newParent, newParent.childNodes.length);
+	}
 }
 
 function outdentNode(node) {
--- a/source.html	Thu May 05 15:20:38 2011 -0600
+++ b/source.html	Thu May 05 15:33:48 2011 -0600
@@ -2005,35 +2005,75 @@
   <!-- Otherwise the last child of the previous sibling might be a list, which
   the li wouldn't get appended to. -->
 
-  <li><span>Indent</span> each member of <var>node list</var>.
+  <li>While <var>node list</var> is not empty:
+
+  <ol>
+    <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>, remove the first member of
+    <var>node list</var> and append it to <var>sublist</var>.
+
+    <li><span>Indent</span> <var>sublist</var>.
+  </ol>
 </ol>
 
-<p>To <dfn>indent</dfn> a [[node]] <var>node</var>:
+<!--
+We have to handle entire lists of siblings at once, or else we'd wind up doing
+something like
+
+  <ol>
+    {<li>foo</li>
+    <ol><li>bar</li></ol>}
+  </ol>
+  ->
+  <ol><ol>
+    <li>foo</li>
+    <li>bar</li>
+  </ol></ol>
+  ->
+  <ol><ol><ol>
+    <li>foo</li>
+    <li>bar</li>
+  </ol></ol></ol>
+
+since by the time we got to doing the <ol> that originally contained "bar", we
+won't remember that we aren't supposed to indent "foo" a second time.
+-->
+<p>To <dfn>indent</dfn> a list <var>node list</var> of consecutive [[sibling]]
+[[nodes]]:
 
 <ol>
-  <li>If <var>node</var>'s [[parent]] is an [[ol]] or [[ul]]:
+  <li>Let <var>first node</var> be the first member of <var>node list</var>.
+
+  <li>If <var>first node</var>'s [[parent]] is an [[ol]] or [[ul]]:
 
   <ol>
     <li>Let <var>tag</var> be the [[localname]] of the [[parent]] of
-    <var>node</var>.
-
-    <li>If the [[previoussibling]] of <var>node</var> is an <span>HTML
-    element</span> with [[localname]] <var>tag</var>, append <var>node</var> as
-    the last [[child]] of its [[previoussibling]], <span>preserving
-    ranges</span>.  Then abort these steps.
-
-    <li>If the [[nextsibling]] of <var>node</var> is an <span>HTML
-    element</span> with [[localname]] <var>tag</var>, insert <var>node</var> as
-    the first [[child]] of its [[nextsibling]], <span>preserving ranges</span>.
+    <var>first node</var>.
+
+    <li>If the [[previoussibling]] of the first member of <var>node list</var>
+    is an <span>HTML element</span> with [[localname]] <var>tag</var>, then for
+    each <var>node</var> in <var>node list</var>, append <var>node</var> as the
+    last [[child]] of its [[previoussibling]], <span>preserving ranges</span>.
     Then abort these steps.
 
+    <li>If the [[nextsibling]] of the last member of <var>node list</var> is an
+    <span>HTML element</span> with [[localname]] <var>tag</var>, then for each
+    <var>node</var> in <var>node list</var> <em>in reverse order</em>, insert
+    <var>node</var> as the first [[child]] of its [[nextsibling]],
+    <span>preserving ranges</span>.  Then abort these steps.
+
     <li>Let <var>new parent</var> be the result of calling <code
     data-anolis-spec=domcore
     title=dom-Document-createElement>createElement(<var>tag</var>)</code> on
-    the [[ownerdocument]] of <var>node</var>.
-
-    <li>Insert <var>new parent</var> into the [[parent]] of <var>node</var>
-    immediately before <var>node</var>.
+    the [[ownerdocument]] of <var>first node</var>.
+
+    <li>Insert <var>new parent</var> into the [[parent]] of <var>first
+    node</var> immediately before <var>first node</var>.
     <!--
     This matches IE9, Firefox 4.0, and Chrome 12 dev.  If there's a preceding
     <li>, Opera 11.10 instead adds the new parent to the end of that <li>, so
@@ -2055,18 +2095,28 @@
     http://www.w3.org/Bugs/Public/show_bug.cgi?id=12609
     -->
 
-    <li>Append <var>node</var> as the last [[child]] of <var>new parent</var>,
+    <li>For each <var>node</var> in <var>node list</var>, append
+    <var>node</var> as the last [[child]] of <var>new parent</var>,
     <span>preserving ranges</span>.
 
     <li>Abort these steps.
   </ol>
 
-  <li>If the [[previoussibling]] of <var>node</var> is an <span>indentation
-  element</span>; its "display" property computes to "block"; its
-  "margin-left" and "margin-right" properties compute to "40px"; and its
-  "margin-top" and "margin-bottom" properties compute to "0"; then append
-  <var>node</var> as the last [[child]] of its [[previoussibling]],
-  <span>preserving ranges</span>, then abort these steps.
+  <li>If the [[previoussibling]] of the first member of <var>node list</var> is
+  an <span>indentation element</span>; its "display" property computes to
+  "block"; its "margin-left" and "margin-right" properties compute to "40px";
+  and its "margin-top" and "margin-bottom" properties compute to "0"; then
+  for each <var>node</var> in <var>node list</var>, append <var>node</var> as
+  the last [[child]] of its [[previoussibling]], <span>preserving
+  ranges</span>.  Then abort these steps.
+
+  <li>If the [[nextsibling]] of the last member of <var>node list</var> is an
+  <span>indentation element</span>; its "display" property computes to "block";
+  its "margin-left" and "margin-right" properties compute to "40px"; and its
+  "margin-top" and "margin-bottom" properties compute to "0"; then for each
+  <var>node</var> in <var>node list</var>, <em>in reverse order</em>, insert
+  <var>node</var> as the first [[child]] of its [[nextsibling]],
+  <span>preserving ranges</span>.  Then abort these steps.
 
   <li>Let <var>tag</var> be "div" if the <span>CSS styling flag</span> is
   true, otherwise "blockquote".
@@ -2081,10 +2131,10 @@
   <li>Let <var>new parent</var> be the result of calling <code
   data-anolis-spec=domcore
   title=dom-Document-createElement>createElement(<var>tag</var>)</code> on
-  the [[ownerdocument]] of <var>node</var>.
+  the [[ownerdocument]] of <var>first node</var>.
 
   <li>Insert <var>new parent</var> into <var>node</var>'s [[parent]]
-  immediately before <var>node</var>.
+  immediately before <var>first node</var>.
 
   <li>Set the CSS property "margin" of <var>new parent</var> to "0 40px".
 
@@ -2105,8 +2155,9 @@
   margins on both sides.  I can't think of any better way to do this at the
   moment.
 
-  <li>Append <var>node</var> as the last [[child]] of <var>new parent</var>,
-  <span>preserving ranges</span>.
+  <li>For each <var>node</var> in <var>node list</var>, append <var>node</var>
+  as the last [[child]] of <var>new parent</var>, <span>preserving
+  ranges</span>.
 </ol>
 
 <dd><strong>State</strong>: